Improved keyboard handling code
[reactos.git] / rosapps / dflat32 / message.c
1 /* --------- message.c ---------- */
2
3 #include "dflat.h"
4 #include "system.h"
5
6 static int handshaking = 0;
7
8 BOOL AllocTesting = FALSE;
9 jmp_buf AllocError;
10 BOOL AltDown = FALSE;
11
12 /* ---------- event queue ---------- */
13 static struct events
14 {
15 DFMESSAGE event;
16 int mx;
17 int my;
18 } EventQueue[MAXMESSAGES];
19
20 /* ---------- message queue --------- */
21 static struct msgs
22 {
23 DFWINDOW wnd;
24 DFMESSAGE msg;
25 PARAM p1;
26 PARAM p2;
27 } MsgQueue[MAXMESSAGES];
28
29 static int EventQueueOnCtr;
30 static int EventQueueOffCtr;
31 static int EventQueueCtr;
32
33 static int MsgQueueOnCtr;
34 static int MsgQueueOffCtr;
35 static int MsgQueueCtr;
36
37
38 DFWINDOW CaptureMouse;
39 DFWINDOW CaptureKeyboard;
40 static BOOL NoChildCaptureMouse;
41 static BOOL NoChildCaptureKeyboard;
42
43 static int doubletimer = -1;
44 static int delaytimer = -1;
45 static int clocktimer = -1;
46
47 static DFWINDOW Cwnd;
48
49 static char ermsg[] = "Error accessing drive x";
50
51
52 static void StopMsg(void)
53 {
54 ClearClipboard();
55 ClearDialogBoxes();
56 restorecursor();
57 unhidecursor();
58 }
59
60 SHORT DfGetScreenHeight (void)
61 {
62 return sScreenHeight;
63 }
64
65 SHORT DfGetScreenWidth (void)
66 {
67 return sScreenWidth;
68 }
69
70 /* ------------ initialize the message system --------- */
71 BOOL DfInitialize (VOID)
72 {
73 CONSOLE_SCREEN_BUFFER_INFO csbi;
74
75 AllocTesting = TRUE;
76 if (setjmp(AllocError) != 0)
77 {
78 StopMsg();
79 return FALSE;
80 }
81
82 /* get input and output handles */
83 hInput = GetStdHandle (STD_INPUT_HANDLE);
84 hOutput = GetStdHandle (STD_OUTPUT_HANDLE);
85
86 /* get screen size */
87 GetConsoleScreenBufferInfo (hOutput, &csbi);
88 sScreenHeight = (csbi.srWindow.Bottom - csbi.srWindow.Top) + 1;
89 sScreenWidth = (csbi.srWindow.Right - csbi.srWindow.Left) + 1;
90
91 /* enable mouse events */
92 SetConsoleMode (hInput, ENABLE_MOUSE_INPUT);
93
94 savecursor();
95 hidecursor();
96
97 CaptureMouse = NULL;
98 CaptureKeyboard = NULL;
99 NoChildCaptureMouse = FALSE;
100 NoChildCaptureKeyboard = FALSE;
101 MsgQueueOnCtr = 0;
102 MsgQueueOffCtr = 0;
103 MsgQueueCtr = 0;
104 EventQueueOnCtr = 0;
105 EventQueueOffCtr = 0;
106 EventQueueCtr = 0;
107 DfPostMessage (NULL, DFM_START, 0, 0);
108
109 return TRUE;
110 }
111
112
113 void DfTerminate (void)
114 {
115
116 }
117
118 /* ----- post an event and parameters to event queue ---- */
119 static void PostEvent(DFMESSAGE event, int p1, int p2)
120 {
121 if (EventQueueCtr != MAXMESSAGES) {
122 EventQueue[EventQueueOnCtr].event = event;
123 EventQueue[EventQueueOnCtr].mx = p1;
124 EventQueue[EventQueueOnCtr].my = p2;
125 if (++EventQueueOnCtr == MAXMESSAGES)
126 EventQueueOnCtr = 0;
127 EventQueueCtr++;
128 }
129 }
130
131 /* ------ collect mouse, clock, and keyboard events ----- */
132 static void collect_events(void)
133 {
134 static int OldShiftKeys = 0;
135 int sk = 0;
136
137 #ifdef TIMER_AVAILABLE
138 static BOOL flipflop = FALSE;
139 static char timestr[9];
140 struct tm *now;
141 int hr;
142 #endif
143
144 HANDLE hInput = GetStdHandle (STD_INPUT_HANDLE);
145 INPUT_RECORD ir;
146 DWORD dwRead;
147 int c;
148
149 #ifdef TIMER_AVAILABLE
150 /* -------- test for a clock event (one/second) ------- */
151 if (timed_out(clocktimer))
152 {
153 /* ----- get the current time ----- */
154 time_t t = time(NULL);
155 now = localtime(&t);
156 hr = now->tm_hour > 12 ?
157 now->tm_hour - 12 :
158 now->tm_hour;
159 if (hr == 0)
160 hr = 12;
161 sprintf(timestr, "%2d:%02d", hr, now->tm_min);
162 strcpy(timestr+5, now->tm_hour > 11 ? "pm " : "am ");
163 /* ------- blink the : at one-second intervals ----- */
164 if (flipflop)
165 *(timestr+2) = ' ';
166 flipflop ^= TRUE;
167 /* -------- reset the timer -------- */
168 set_timer(clocktimer, 1);
169 /* -------- post the clock event -------- */
170 PostEvent(CLOCKTICK, (PARAM)timestr, 0);
171 }
172 #endif
173
174 // WaitForSingleObject (hInput, INFINITE);
175 ReadConsoleInput (hInput, &ir, 1, &dwRead);
176
177 if ((ir.EventType == KEY_EVENT) &&
178 (ir.Event.KeyEvent.bKeyDown == TRUE))
179 {
180 /* handle key down events */
181
182 /* handle shift state changes */
183 if (ir.Event.KeyEvent.dwControlKeyState &
184 (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
185 {
186 sk |= ALTKEY;
187 }
188 if (ir.Event.KeyEvent.dwControlKeyState &
189 (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
190 {
191 sk |= CTRLKEY;
192 }
193 if (ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
194 {
195 sk |= LEFTSHIFT + RIGHTSHIFT;
196 }
197
198 if (sk != OldShiftKeys)
199 {
200 OldShiftKeys = sk;
201 /* the shift status changed */
202 PostEvent(SHIFT_CHANGED, sk, 0);
203 #if 0
204 if (sk & ALTKEY)
205 AltDown = TRUE;
206 else
207 AltDown = FALSE;
208 #endif
209 }
210
211 if (ir.Event.KeyEvent.uChar.AsciiChar == 0)
212 {
213 switch (ir.Event.KeyEvent.wVirtualKeyCode)
214 {
215 case VK_F1:
216 c = F1;
217 break;
218
219 case VK_F4:
220 if (sk & ALTKEY)
221 c = ALT_F4;
222 else if (sk & CTRLKEY)
223 c = CTRL_F4;
224 else
225 c = F4;
226
227 case VK_F10:
228 c = F10;
229 break;
230
231 case VK_UP:
232 c = UP;
233 break;
234
235 case VK_DOWN:
236 c = DN;
237 break;
238
239 case VK_LEFT:
240 c = BS;
241 break;
242
243 case VK_RIGHT:
244 c = FWD;
245 break;
246
247 case VK_INSERT:
248 c = INS;
249 break;
250
251 case VK_DELETE:
252 c = DEL;
253 break;
254
255 case VK_HOME:
256 c = HOME;
257 break;
258
259 case VK_END:
260 c = END;
261 break;
262
263 case VK_PRIOR:
264 c = PGUP;
265 break;
266
267 case VK_NEXT:
268 c = PGDN;
269 break;
270
271 default:
272 return;
273 }
274 }
275 else
276 {
277 /* special handling of SHIFT+TAB */
278 if (ir.Event.KeyEvent.uChar.AsciiChar == VK_TAB &&
279 (ir.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED))
280 c = SHIFT_HT;
281 else
282 c = ir.Event.KeyEvent.uChar.AsciiChar;
283 }
284
285 PostEvent (KEYBOARD, c, sk);
286 }
287 else if (ir.EventType == MOUSE_EVENT)
288 {
289 /* handle mouse events */
290 if (ir.Event.MouseEvent.dwEventFlags == MOUSE_MOVED)
291 {
292 PostEvent (MOUSE_MOVED,
293 ir.Event.MouseEvent.dwMousePosition.X,
294 ir.Event.MouseEvent.dwMousePosition.Y);
295 }
296 else if (ir.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK)
297 {
298 if (ir.Event.MouseEvent.dwButtonState ==
299 FROM_LEFT_1ST_BUTTON_PRESSED)
300 {
301 PostEvent (DOUBLE_CLICK,
302 ir.Event.MouseEvent.dwMousePosition.X,
303 ir.Event.MouseEvent.dwMousePosition.Y);
304 }
305 }
306 else if (ir.Event.MouseEvent.dwEventFlags == 0)
307 {
308 /* single click */
309 if (ir.Event.MouseEvent.dwButtonState ==
310 FROM_LEFT_1ST_BUTTON_PRESSED)
311 {
312 PostEvent (LEFT_BUTTON,
313 ir.Event.MouseEvent.dwMousePosition.X,
314 ir.Event.MouseEvent.dwMousePosition.Y);
315 }
316 else if (ir.Event.MouseEvent.dwButtonState ==
317 RIGHTMOST_BUTTON_PRESSED)
318 {
319 PostEvent (RIGHT_BUTTON,
320 ir.Event.MouseEvent.dwMousePosition.X,
321 ir.Event.MouseEvent.dwMousePosition.Y);
322 }
323 else if (ir.Event.MouseEvent.dwButtonState == 0)
324 {
325 PostEvent (DFM_BUTTON_RELEASED,
326 ir.Event.MouseEvent.dwMousePosition.X,
327 ir.Event.MouseEvent.dwMousePosition.Y);
328 }
329 }
330 }
331 }
332
333
334 /* ----- post a message and parameters to msg queue ---- */
335 void DfPostMessage(DFWINDOW wnd, DFMESSAGE msg, PARAM p1, PARAM p2)
336 {
337 if (msg == ENDDIALOG)
338 {
339 msg++;
340 --msg;
341 }
342
343 if (MsgQueueCtr != MAXMESSAGES)
344 {
345 MsgQueue[MsgQueueOnCtr].wnd = wnd;
346 MsgQueue[MsgQueueOnCtr].msg = msg;
347 MsgQueue[MsgQueueOnCtr].p1 = p1;
348 MsgQueue[MsgQueueOnCtr].p2 = p2;
349 if (++MsgQueueOnCtr == MAXMESSAGES)
350 MsgQueueOnCtr = 0;
351 MsgQueueCtr++;
352 }
353 }
354
355 /* --------- send a message to a window ----------- */
356 int DfSendMessage(DFWINDOW wnd, DFMESSAGE msg, PARAM p1, PARAM p2)
357 {
358 int rtn = TRUE, x, y;
359
360 #ifdef INCLUDE_LOGGING
361 LogMessages(wnd, msg, p1, p2);
362 #endif
363 if (wnd != NULL)
364 switch (msg) {
365 case PAINT:
366 case BORDER:
367 /* ------- don't send these messages unless the
368 window is visible -------- */
369 if (isVisible(wnd))
370 rtn = (*wnd->wndproc)(wnd, msg, p1, p2);
371 break;
372 case RIGHT_BUTTON:
373 case LEFT_BUTTON:
374 case DOUBLE_CLICK:
375 case DFM_BUTTON_RELEASED:
376 /* --- don't send these messages unless the
377 window is visible or has captured the mouse -- */
378 if (isVisible(wnd) || wnd == CaptureMouse)
379 rtn = (*wnd->wndproc)(wnd, msg, p1, p2);
380 break;
381 case KEYBOARD:
382 case SHIFT_CHANGED:
383 /* ------- don't send these messages unless the
384 window is visible or has captured the keyboard -- */
385 if (!(isVisible(wnd) || wnd == CaptureKeyboard))
386 break;
387 default:
388 rtn = (*wnd->wndproc)(wnd, msg, p1, p2);
389 break;
390 }
391 /* ----- window processor returned true or the message was sent
392 to no window at all (NULL) ----- */
393 if (rtn != FALSE) {
394 /* --------- process messages that a window sends to the
395 system itself ---------- */
396 switch (msg) {
397 case DFM_STOP:
398 StopMsg();
399 break;
400 /* ------- clock messages --------- */
401 case CAPTURE_CLOCK:
402 Cwnd = wnd;
403 set_timer(clocktimer, 0);
404 break;
405 case RELEASE_CLOCK:
406 Cwnd = NULL;
407 disable_timer(clocktimer);
408 break;
409 /* -------- keyboard messages ------- */
410 case KEYBOARD_CURSOR:
411 if (wnd == NULL)
412 cursor((int)p1, (int)p2);
413 else if (wnd == inFocus)
414 cursor(GetClientLeft(wnd)+(int)p1,
415 GetClientTop(wnd)+(int)p2);
416 break;
417 case CAPTURE_KEYBOARD:
418 if (p2)
419 ((DFWINDOW)p2)->PrevKeyboard=CaptureKeyboard;
420 else
421 wnd->PrevKeyboard = CaptureKeyboard;
422 CaptureKeyboard = wnd;
423 NoChildCaptureKeyboard = (int)p1;
424 break;
425 case RELEASE_KEYBOARD:
426 if (wnd != NULL)
427 {
428 if (CaptureKeyboard == wnd || (int)p1)
429 CaptureKeyboard = wnd->PrevKeyboard;
430 else
431 {
432 DFWINDOW twnd = CaptureKeyboard;
433 while (twnd != NULL)
434 {
435 if (twnd->PrevKeyboard == wnd)
436 {
437 twnd->PrevKeyboard = wnd->PrevKeyboard;
438 break;
439 }
440 twnd = twnd->PrevKeyboard;
441 }
442 if (twnd == NULL)
443 CaptureKeyboard = NULL;
444 }
445 wnd->PrevKeyboard = NULL;
446 }
447 else
448 CaptureKeyboard = NULL;
449 NoChildCaptureKeyboard = FALSE;
450 break;
451 case CURRENT_KEYBOARD_CURSOR:
452 curr_cursor(&x, &y);
453 *(int*)p1 = x;
454 *(int*)p2 = y;
455 break;
456 case SAVE_CURSOR:
457 savecursor();
458 break;
459 case RESTORE_CURSOR:
460 restorecursor();
461 break;
462 case HIDE_CURSOR:
463 normalcursor();
464 hidecursor();
465 break;
466 case SHOW_CURSOR:
467 if (p1)
468 set_cursor_size(100);
469 else
470 set_cursor_size(5);
471 unhidecursor();
472 break;
473
474 case CAPTURE_MOUSE:
475 if (p2)
476 ((DFWINDOW)p2)->PrevMouse = CaptureMouse;
477 else
478 wnd->PrevMouse = CaptureMouse;
479 CaptureMouse = wnd;
480 NoChildCaptureMouse = (int)p1;
481 break;
482
483 case RELEASE_MOUSE:
484 if (wnd != NULL)
485 {
486 if (CaptureMouse == wnd || (int)p1)
487 CaptureMouse = wnd->PrevMouse;
488 else
489 {
490 DFWINDOW twnd = CaptureMouse;
491 while (twnd != NULL)
492 {
493 if (twnd->PrevMouse == wnd)
494 {
495 twnd->PrevMouse = wnd->PrevMouse;
496 break;
497 }
498 twnd = twnd->PrevMouse;
499 }
500 if (twnd == NULL)
501 CaptureMouse = NULL;
502 }
503 wnd->PrevMouse = NULL;
504 }
505 else
506 CaptureMouse = NULL;
507 NoChildCaptureMouse = FALSE;
508 break;
509
510 default:
511 break;
512 }
513 }
514 return rtn;
515 }
516
517 static DFRECT VisibleRect(DFWINDOW wnd)
518 {
519 DFRECT rc = WindowRect(wnd);
520 if (!TestAttribute(wnd, NOCLIP))
521 {
522 DFWINDOW pwnd = GetParent(wnd);
523 DFRECT prc;
524 prc = ClientRect(pwnd);
525 while (pwnd != NULL)
526 {
527 if (TestAttribute(pwnd, NOCLIP))
528 break;
529 rc = subRectangle(rc, prc);
530 if (!ValidRect(rc))
531 break;
532 if ((pwnd = GetParent(pwnd)) != NULL)
533 prc = ClientRect(pwnd);
534 }
535 }
536 return rc;
537 }
538
539 /* ----- find window that mouse coordinates are in --- */
540 static DFWINDOW inWindow(DFWINDOW wnd, int x, int y)
541 {
542 DFWINDOW Hit = NULL;
543 while (wnd != NULL) {
544 if (isVisible(wnd)) {
545 DFWINDOW wnd1;
546 DFRECT rc = VisibleRect(wnd);
547 if (InsideRect(x, y, rc))
548 Hit = wnd;
549 if ((wnd1 = inWindow(LastWindow(wnd), x, y)) != NULL)
550 Hit = wnd1;
551 if (Hit != NULL)
552 break;
553 }
554 wnd = PrevWindow(wnd);
555 }
556 return Hit;
557 }
558
559 static DFWINDOW MouseWindow(int x, int y)
560 {
561 /* get the window in which a mouse event occurred */
562 DFWINDOW Mwnd = inWindow(ApplicationWindow, x, y);
563
564 /* ---- process mouse captures ----- */
565 if (CaptureMouse != NULL)
566 {
567 if (NoChildCaptureMouse ||
568 Mwnd == NULL ||
569 !isAncestor(Mwnd, CaptureMouse))
570 Mwnd = CaptureMouse;
571 }
572 return Mwnd;
573 }
574
575
576 void handshake(void)
577 {
578 handshaking++;
579 DfDispatchMessage ();
580 --handshaking;
581 }
582
583
584 /* ---- dispatch messages to the message proc function ---- */
585 BOOL DfDispatchMessage (void)
586 {
587 DFWINDOW Mwnd, Kwnd;
588
589 /* -------- collect mouse and keyboard events ------- */
590 collect_events();
591
592 /* --------- dequeue and process events -------- */
593 while (EventQueueCtr > 0)
594 {
595 struct events ev;
596
597 ev = EventQueue[EventQueueOffCtr];
598 if (++EventQueueOffCtr == MAXMESSAGES)
599 EventQueueOffCtr = 0;
600 --EventQueueCtr;
601
602 /* get the window in which a keyboard event occurred */
603 Kwnd = inFocus;
604
605 /* process keyboard captures */
606 if (CaptureKeyboard != NULL)
607 {
608 if (Kwnd == NULL ||
609 NoChildCaptureKeyboard ||
610 !isAncestor(Kwnd, CaptureKeyboard))
611 Kwnd = CaptureKeyboard;
612 }
613
614 /* send mouse and keyboard messages to the
615 window that should get them */
616 switch (ev.event)
617 {
618 case SHIFT_CHANGED:
619 case KEYBOARD:
620 if (!handshaking)
621 DfSendMessage(Kwnd, ev.event, ev.mx, ev.my);
622 break;
623
624 case LEFT_BUTTON:
625 if (!handshaking)
626 {
627 Mwnd = MouseWindow(ev.mx, ev.my);
628 if (!CaptureMouse ||
629 (!NoChildCaptureMouse &&
630 isAncestor(Mwnd, CaptureMouse)))
631 {
632 if (Mwnd != inFocus)
633 DfSendMessage(Mwnd, SETFOCUS, TRUE, 0);
634 DfSendMessage(Mwnd, LEFT_BUTTON, ev.mx, ev.my);
635 }
636 }
637 break;
638
639 case DFM_BUTTON_RELEASED:
640 case DOUBLE_CLICK:
641 case RIGHT_BUTTON:
642 if (handshaking)
643 break;
644
645 case MOUSE_MOVED:
646 Mwnd = MouseWindow(ev.mx, ev.my);
647 DfSendMessage(Mwnd, ev.event, ev.mx, ev.my);
648 break;
649
650 case CLOCKTICK:
651 DfSendMessage(Cwnd, ev.event, ev.mx, ev.my);
652 break;
653
654 default:
655 break;
656 }
657 }
658
659 /* ------ dequeue and process messages ----- */
660 while (MsgQueueCtr > 0)
661 {
662 struct msgs mq;
663
664 mq = MsgQueue[MsgQueueOffCtr];
665
666 if (++MsgQueueOffCtr == MAXMESSAGES)
667 MsgQueueOffCtr = 0;
668 --MsgQueueCtr;
669
670 DfSendMessage (mq.wnd, mq.msg, mq.p1, mq.p2);
671 if (mq.msg == ENDDIALOG)
672 return FALSE;
673
674 if (mq.msg == DFM_STOP)
675 return FALSE;
676 }
677
678 return TRUE;
679 }
680
681 /* EOF */