Test initialisation of MUIA_List_AdjustWidth and MUIA_List_AdjustHeight, and
[AROS.git] / arch / all-mingw32 / hidd / wingdi / gdi_native.c
blob6f3a57371f543ae7b1a76f502ead80df317d0f20
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Host-side part of GDI hidd. Handles windows and receives events.
6 Lang: English.
7 */
9 #include <windows.h>
10 #include <windowsx.h>
11 #include <stdio.h>
12 #include <aros/irq.h>
14 #include "gdi.h"
15 #include "bitmap.h"
17 #define D(x)
18 #define DACT(x)
19 #define DKBD(x)
20 #define DMSE(x)
21 #define DWIN(x)
23 LRESULT CALLBACK display_callback(HWND win, UINT msg, WPARAM wp, LPARAM lp);
24 LRESULT CALLBACK bitmap_callback(HWND win, UINT msg, WPARAM wp, LPARAM lp);
26 /* Global variables shared by all windows (theoretical) */
28 static WNDCLASS display_class_desc =
30 CS_NOCLOSE,
31 display_callback,
34 NULL,
35 NULL,
36 NULL,
37 (HBRUSH)(COLOR_WINDOW + 1),
38 NULL,
39 TEXT("AROS_Screen")
42 static WNDCLASS bitmap_class_desc =
44 CS_NOCLOSE,
45 bitmap_callback,
48 NULL,
49 NULL,
50 NULL,
51 (HBRUSH)(COLOR_WINDOW + 1),
52 NULL,
53 TEXT("AROS_Bitmap")
56 static ULONG_PTR display_class; /* Display window class */
57 static ULONG_PTR bitmap_class; /* Bitmap window class */
58 static HWND window_active; /* Currently active AROS window */
59 static HANDLE KbdAck; /* Keyboard acknowledge event */
60 static HANDLE MouseAck; /* Mouse acknowledge event */
61 static DWORD thread_id; /* Window service thread ID */
62 static DWORD last_key; /* Last pressed key - used to suppress autorepeat */
64 /* Virtual hardware registers */
65 static volatile struct GDI_Control gdictl;
67 /****************************************************************************************/
69 static ULONG SendKbdIRQ(UINT msg, DWORD key)
71 DKBD(printf("[GDI] Keyboard event 0x%04lX key 0x%04lX\n", msg, key));
74 * Lock until AROS have read the previous event.
75 * This should fix swallowing or duplicating events, which happens because
76 * of race condition between this thread and AROS:
77 * 1. We signal a keypress.
78 * 2. AROS gets a signal and wakes up. But has no time to read the event, Windows context switches.
79 * 3. We get one more keypress, send another event.
80 * 4. AROS wakes up again, reads event. Note that this is already another event (3).
81 * 5. AROS sees one more event, and reads it again. Result: first keypress lost, second duplicated.
82 * Note that this event is set to signalled state during creation.
84 * Be careful! Doing this here means AROS MUST read events and reset locks. Otherwise we'll deadlock
85 * here upon the second event. Never dispose a keyboard or mouse HIDD objects, for example. There's
86 * no need to, but who knows...
88 * In case of futrher problems a FIFO event queue needs to be implemented here.
90 WaitForSingleObject(KbdAck, INFINITE);
92 gdictl.KbdEvent = msg;
93 gdictl.KeyCode = key;
95 return KrnCauseIRQ(gdictl.KbdIrq);
98 /* We have to use this weird hook technique because there's no other way to prevent "Win" keys
99 from opening theif stupid "Start menu". */
100 LRESULT CALLBACK key_callback(int code, WPARAM wp, KBDLLHOOKSTRUCT *lp)
102 DWORD key;
104 if ((code == HC_ACTION) && window_active)
106 wp &= 0xFFFFFFFB; /* This masks out difference between WM_SYSKEY* and WM_KEY* */
107 key = (lp->scanCode & 0xFF) | ((lp->flags & LLKHF_EXTENDED) << 8);
110 * Here we get raw keypresses, including autorepeats. We have to sort out
111 * autorepeats in some smart way.
113 switch(wp)
115 case WM_KEYDOWN:
116 if (key == last_key)
117 return 1;
118 last_key = key;
119 break;
121 case WM_KEYUP:
122 if (key == last_key)
123 last_key = 0;
125 SendKbdIRQ(wp, key);
126 return 1;
128 return CallNextHookEx(NULL, code, wp, (LPARAM)lp);
131 LRESULT CALLBACK display_callback(HWND win, UINT msg, WPARAM wp, LPARAM lp)
133 struct gfx_data *gdata;
135 switch(msg)
137 case WM_SETCURSOR:
138 gdata = (struct gfx_data *)GetWindowLongPtr(win, GWLP_USERDATA);
140 if (gdata->cursor)
142 SetCursor(gdata->cursor);
143 return 0;
145 break;
147 case WM_MOUSEMOVE:
148 case WM_LBUTTONDOWN:
149 case WM_LBUTTONUP:
150 case WM_RBUTTONDOWN:
151 case WM_RBUTTONUP:
152 case WM_MBUTTONDOWN:
153 case WM_MBUTTONUP:
154 case WM_MOUSEWHEEL:
155 if (win == window_active)
157 DMSE(printf("[GDI] Mouse event 0x%04X, window 0x%p\n", msg, win));
159 /* Lock to prevent event loss. See SendKbdIRQ() above for explanation. */
160 WaitForSingleObject(MouseAck, INFINITE);
162 gdictl.MouseEvent = msg;
163 gdictl.MouseX = GET_X_LPARAM(lp);
164 gdictl.MouseY = GET_Y_LPARAM(lp);
165 gdictl.Buttons = wp & 0x0000FFFF;
166 gdictl.WheelDelta = wp >> 16;
167 KrnCauseIRQ(gdictl.MouseIrq);
169 return 0;
171 #ifndef __x86_64__
173 * This keyboard-related fragment is not used on Windows NT because keyboard hook
174 * intercepts all keyboard messages. It is left here for Windows 9x.
176 case WM_KEYDOWN:
177 case WM_SYSKEYDOWN:
178 if (lp & 0x40000000) /* Ignore autorepeats */
179 return 0;
181 case WM_KEYUP:
182 case WM_SYSKEYUP:
183 SendKbdIRQ(msg & 0xFFFFFFFB, (lp >> 16) & 0x000001FF);
184 return 0;
185 #endif
187 case WM_ACTIVATE:
188 DACT(printf("[GDI] WM_ACTIVATE, Window 0x%p, wParam 0x%08lX\n", win, wp));
189 /* In some cases Windows can activate an iconified window (for example if we minimize it
190 by clicking its button on the taskbar). We process deactivation messages regardless of
191 minimized state, but we handle activation only when it's done on a non-minimized window.
192 This behavior was discovered by trial and error, i hope it's really ok now. */
193 if ((wp & 0x0000FFFF) != WA_INACTIVE)
195 if (!(wp & 0xFFFF0000))
197 window_active = win;
198 gdictl.Active = (void *)GetWindowLongPtr(win, GWLP_USERDATA);
199 KrnCauseIRQ(gdictl.GfxIrq);
202 else
204 window_active = NULL;
206 /* Send WM_KEYUP in order to prevent "stuck keys" phenomena */
207 if (last_key)
209 SendKbdIRQ(WM_KEYUP, last_key);
210 last_key = 0;
213 break;
215 return DefWindowProc(win, msg, wp, lp);
218 LRESULT CALLBACK bitmap_callback(HWND win, UINT msg, WPARAM wp, LPARAM lp)
220 HDC window_dc;
221 PAINTSTRUCT ps;
222 LONG x, y, xsize, ysize;
223 struct bitmap_data *bmdata;
225 switch(msg)
227 case WM_PAINT:
228 bmdata = (struct bitmap_data *)GetWindowLongPtr(win, GWLP_USERDATA);
229 window_dc = BeginPaint(win, &ps);
230 x = ps.rcPaint.left;
231 y = ps.rcPaint.top;
232 xsize = ps.rcPaint.right - ps.rcPaint.left + 1;
233 ysize = ps.rcPaint.bottom - ps.rcPaint.top + 1;
234 BitBlt(window_dc, x, y, xsize, ysize, bmdata->dc, x, y, SRCCOPY);
235 EndPaint(win, &ps);
236 return 0;
238 default:
239 return DefWindowProc(win, msg, wp, lp);
243 DWORD WINAPI gdithread_entry(struct GDI_Control *ctl)
245 HHOOK keyhook;
246 BOOL res;
247 MSG msg;
248 struct gfx_data *gdata;
249 struct bitmap_data *bmdata;
250 LONG width, height;
251 HWND prev;
253 ctl->KbdEvent = 0; /* Just in case... */
254 ctl->MouseEvent = 0;
256 keyhook = SetWindowsHookEx(WH_KEYBOARD_LL, (HOOKPROC)key_callback, display_class_desc.hInstance, 0);
260 res = GetMessage(&msg, NULL, 0, 0);
261 D(printf("[GDI] GetMessage returned %ld\n", res));
263 if (res > 0)
265 D(printf("[GDI] Message %lu for window 0x%p\n", msg.message, msg.hwnd));
267 switch (msg.message)
269 case NOTY_SHOW:
270 gdata = (struct gfx_data *)msg.wParam;
271 DWIN(printf("[GDI] NOTY_SHOW, Display data: 0x%p\n", gdata));
272 width = 0;
273 height = 0;
274 prev = HWND_TOP;
276 /* Traverse through the bitmaps list and (re)open every bitmap's window. This will
277 cause rearranging them in the correct Z-order */
278 for (bmdata = (struct bitmap_data *)gdata->bitmaps.mlh_Head;
279 bmdata->node.mln_Succ; bmdata = (struct bitmap_data *)bmdata->node.mln_Succ)
282 /* Display window dimensions based on its size will be calculated only for the
283 first (frontmost) bitmap */
284 if (!width)
286 width = GetSystemMetrics(SM_CXFIXEDFRAME) * 2 + bmdata->win_width;
287 height = GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + bmdata->win_height + GetSystemMetrics(SM_CYCAPTION);
289 DWIN(printf("[GDI] Display window: 0x%p\n", gdata->fbwin));
290 if (gdata->fbwin)
292 DWIN(printf("[GDI] Resizing display...\n"));
293 SetWindowPos(gdata->fbwin, 0, 0, 0, width, height, SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOZORDER);
297 /* Open display window if we don't have it. Try every time, for sure */
298 if (!gdata->fbwin)
300 DWIN(printf("[GDI] Opening display...\n"));
303 * We create the window as invisible, and show it only after setting gdata pointer.
304 * Otherwise we miss activation event (since gdata is NULL)
306 gdata->fbwin = CreateWindow((LPCTSTR)display_class, TEXT("AROS Screen"), WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX,
307 CW_USEDEFAULT, CW_USEDEFAULT, width, height, NULL, NULL,
308 display_class_desc.hInstance, NULL);
309 DWIN(printf("[GDI] Opened display window: 0x%p\n", gdata->fbwin));
310 if (gdata->fbwin)
312 SetWindowLongPtr(gdata->fbwin, GWLP_USERDATA, (LONG_PTR)gdata);
313 ShowWindow(gdata->fbwin, SW_SHOW);
317 /* Open bitmap window only if we really have display window */
318 if (gdata->fbwin)
320 DWIN(printf("[GDI] Showing bitmap data 0x%p, window 0x%p\n", bmdata, bmdata->window));
323 * WS_DISABLED here causes forwarding all input to the parent window (i. e. display window).
324 * In future we may need some more sophisticated input handling because current driver architecture
325 * allows further transformation to rootless mode where every screen will have its own separate window
326 * on a Windows desktop.
328 if (!bmdata->window)
330 bmdata->window = CreateWindow((LPCTSTR)bitmap_class, NULL, WS_BORDER|WS_CHILD|WS_CLIPSIBLINGS|WS_DISABLED, bmdata->bm_left - 1, bmdata->bm_top - 1, bmdata->bm_width + 2, bmdata->bm_height + 2,
331 gdata->fbwin, NULL, display_class_desc.hInstance, NULL);
332 DWIN(printf("[GDI] Opened bitmap window: 0x%p\n", bmdata->window));
335 if (bmdata->window)
337 SetWindowLongPtr(bmdata->window, GWLP_USERDATA, (LONG_PTR)bmdata);
339 * We actually show the window only now, because otherwise it will pop up in front of all windows, and then
340 * immediately jump backwards, causing irritating flicker
342 SetWindowPos(bmdata->window, prev, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
343 UpdateWindow(bmdata->window);
345 prev = bmdata->window;
350 /* If we got no bitmaps, close the display window */
351 if ((prev == HWND_TOP) && gdata->fbwin)
353 DestroyWindow(gdata->fbwin);
354 gdata->fbwin = NULL;
357 ctl->ShowDone = TRUE;
358 KrnCauseIRQ(ctl->GfxIrq);
359 break;
361 default:
362 DispatchMessage(&msg);
363 break;
366 } while (res > 0);
368 /* TODO: Further cleanup (close windows etc) */
370 if (keyhook)
371 UnhookWindowsHookEx(keyhook);
373 return 0;
376 /****************************************************************************************/
378 volatile struct GDI_Control *__declspec(dllexport) __aros GDI_Init(void)
380 long irq;
381 HANDLE th;
383 irq = KrnAllocIRQ();
384 if (irq != -1)
386 gdictl.GfxIrq = irq;
387 irq = KrnAllocIRQ();
388 if (irq != -1)
390 gdictl.KbdIrq = irq;
391 irq = KrnAllocIRQ();
392 if (irq != -1)
394 gdictl.MouseIrq = irq;
396 display_class_desc.hInstance = GetModuleHandle(NULL);
397 display_class_desc.hIcon = LoadIcon(display_class_desc.hInstance, MAKEINTRESOURCE(101));
398 display_class_desc.hCursor = LoadCursor(NULL, IDC_ARROW);
399 display_class = RegisterClass(&display_class_desc);
400 D(printf("[GDI] Created display window class 0x%04x\n", display_class));
402 if (display_class)
404 bitmap_class_desc.hInstance = display_class_desc.hInstance;
405 bitmap_class = RegisterClass(&bitmap_class_desc);
406 D(printf("[GDI] Created bitmap window class 0x%04x\n", bitmap_class));
408 if (bitmap_class)
410 KbdAck = CreateEvent(NULL, FALSE, TRUE, NULL);
411 if (KbdAck)
413 MouseAck = CreateEvent(NULL, FALSE, TRUE, NULL);
414 if (MouseAck)
416 gdictl.ShowDone = FALSE;
417 gdictl.Active = NULL;
419 window_active = NULL;
420 last_key = 0;
421 th = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)gdithread_entry, (void *)&gdictl, 0, &thread_id);
423 D(printf("[GDI] Started thread 0x%p ID 0x%08lX\n", th, thread_id));
424 if (th)
426 CloseHandle(th);
427 return &gdictl;
429 CloseHandle(MouseAck);
431 CloseHandle(KbdAck);
433 UnregisterClass((LPCTSTR)bitmap_class, bitmap_class_desc.hInstance);
435 UnregisterClass((LPCTSTR)display_class, display_class_desc.hInstance);
437 KrnFreeIRQ(gdictl.MouseIrq);
439 KrnFreeIRQ(gdictl.KbdIrq);
441 KrnFreeIRQ(gdictl.GfxIrq);
443 return NULL;
446 void __declspec(dllexport) __aros GDI_Shutdown(struct GDI_Control *ctl)
448 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
449 UnregisterClass((LPCTSTR)bitmap_class, bitmap_class_desc.hInstance);
450 UnregisterClass((LPCTSTR)display_class, display_class_desc.hInstance);
451 CloseHandle(MouseAck);
452 CloseHandle(KbdAck);
453 KrnFreeIRQ(ctl->MouseIrq);
454 KrnFreeIRQ(ctl->KbdIrq);
455 KrnFreeIRQ(ctl->GfxIrq);
458 /* I'm too lazy to import one more .dll (kernel32). I'd better write these stubs. */
459 ULONG __declspec(dllexport) __aros GDI_PutMsg(void *window, UINT msg, WPARAM wp, LPARAM lp)
461 if (window)
462 return PostMessage(window, msg, wp, lp);
463 else
464 return PostThreadMessage(thread_id, msg, wp, lp);
467 ULONG __declspec(dllexport) __aros GDI_KbdAck(void)
469 return SetEvent(KbdAck);
472 ULONG __declspec(dllexport) __aros GDI_MouseAck(void)
474 return SetEvent(MouseAck);