MODULE WinGui;
(**
	project	= "BlackBox 2.0"
	organization	= "www.oberon.ch blackbox.oberon.org"
	contributors	= "Oberon microsystems, Anton Dmitriev, Ivan Denisov"
	version	= "System/Rsrc/About"
	copyright	= "System/Rsrc/About"
	license	= "The 2-Clause BSD License"
	changes	= "
	- YYYYMMDD, nn, ...
	"
	issues	= "
	- ...
	"

**)

	(* Any application using the GUI on Windows should has this module linked. *)

	IMPORT SYSTEM, Kernel, WinApi, WinFiles, WinKernel;
	
	VAR
		mainWnd*, unit*: INTEGER;
	
	PROCEDURE MessageBox* (title, msg: ARRAY OF CHAR; buttons: SET): INTEGER;
		VAR res: INTEGER;
	BEGIN
		res := WinApi.MessageBoxW(mainWnd, msg, title, buttons);
		IF res = 0 THEN (* win2000/XP *)
			res := WinApi.LoadLibraryA("COMCTL32.dll");
			res := WinApi.MessageBoxW(mainWnd, msg, title, buttons)
		END;
		RETURN res
	END MessageBox;

	(* -------------------- keyboard interrupt handling --------------------- *)

	PROCEDURE KeyboardWatcher (main: INTEGER): INTEGER;	(* runs in a thread *)
		TYPE P = PROCEDURE (w: INTEGER): INTEGER;
		VAR res, a: INTEGER; top, h: WinApi.HANDLE; done: BOOLEAN;
			context: WinApi.CONTEXT; mod: Kernel.Module; isHungAppWindow: P;
	BEGIN
		done := FALSE;
		isHungAppWindow := NIL;
		h := WinApi.LoadLibraryA("user32.dll");
		IF h # 0 THEN
			isHungAppWindow := SYSTEM.VAL(P, WinApi.GetProcAddress(h, "IsHungAppWindow"))
		END;
		LOOP
			res := WinApi.GetAsyncKeyState(WinApi.VK_CANCEL);
			IF res >= 0 THEN	(* key released *)
				done := FALSE;
				WinApi.Sleep(10);
			ELSIF ~done THEN	(* key pressed *)
				top := WinApi.GetForegroundWindow();
				IF (mainWnd # top) & (isHungAppWindow # NIL)
						& (top # 0) & (isHungAppWindow(top) # 0)
						& (mainWnd # 0) & (isHungAppWindow(mainWnd) # 0) THEN
					(* main window replaced by ghost window *)
					(* check if application window is topmost nonresponding window *)
					REPEAT
						top := WinApi.GetWindow(top, WinApi.GW_HWNDNEXT)
					UNTIL (top = 0) OR (isHungAppWindow(top) # 0) & (WinApi.IsWindowVisible(top) # 0)
				END;
				IF mainWnd = top THEN
					res := WinApi.SuspendThread(main);
					context.ContextFlags := {0, 16};
					res := WinApi.GetThreadContext(main, context);
					mod := Kernel.modList; a := context.Eip;
					WHILE (mod # NIL) & ((a < mod.code) OR (a >= mod.code + mod.csize)) DO
						mod := mod.next
					END;
					IF (mod # NIL) & (mod.name = "Kernel") THEN mod := NIL END;
					IF mod # NIL THEN
						WinKernel.interrupted := TRUE;
						INCL(SYSTEM.VAL(SET, context.EFlags), 8);	(* set trap flag *)
						res := WinApi.SetThreadContext(main, context);
						done := TRUE
					END;
					res := WinApi.ResumeThread(main);
					WinApi.Sleep(1);
					WinKernel.interrupted := FALSE
				END
			END
		END;
		RETURN 0
	END KeyboardWatcher;

	PROCEDURE InstallCtrlBreakInterrupt*;
		VAR res, id: INTEGER; t, main: WinApi.HANDLE;
	BEGIN
		res := WinApi.DuplicateHandle(WinApi.GetCurrentProcess(), WinApi.GetCurrentThread(),
					WinApi.GetCurrentProcess(), main, {1, 3, 4, 16..19}, 0, {});
		t := WinApi.CreateThread(NIL, 4096, KeyboardWatcher, main, {}, id);
	END InstallCtrlBreakInterrupt;

	PROCEDURE Init;
	VAR res_: INTEGER;
	BEGIN
		WinFiles.MessageBox := NIL;
		(*
			use this code,
				if you want framework ask confirmation from user for creation of new directories
			WinFiles.MessageBox := MessageBox;
		*)
		WinKernel.SetMsgBoxProc(MessageBox);
	END Init;

BEGIN
	Init
END WinGui.