1 /* Clutter.
2 * An OpenGL based 'interactive canvas' library.
3 * Authored By Matthew Allum <mallum@openedhand.com>
4 * Copyright (C) 2006-2007 OpenedHand
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 *
19 *
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "clutter-stage-win32.h"
27 #include "clutter-backend-win32.h"
28 #include "clutter-win32.h"
29
30 #include "clutter-backend.h"
31 #include "clutter-debug.h"
32 #include "clutter-device-manager-private.h"
33 #include "clutter-event-private.h"
34 #include "clutter-keysyms.h"
35 #include "clutter-main.h"
36 #include "clutter-private.h"
37 #include "clutter-stage-private.h"
38
39 #include <string.h>
40 #include <glib.h>
41 #include <windows.h>
42 #include <windowsx.h>
43 #include <stdlib.h>
44
45 typedef struct _ClutterEventSource ClutterEventSource;
46
47 struct _ClutterEventSource
48 {
49 GSource source;
50
51 ClutterBackend *backend;
52 GPollFD event_poll_fd;
53 };
54
55 static gboolean clutter_event_prepare (GSource *source,
56 gint *timeout);
57 static gboolean clutter_event_check (GSource *source);
58 static gboolean clutter_event_dispatch (GSource *source,
59 GSourceFunc callback,
60 gpointer user_data);
61
62 static GSourceFuncs event_funcs = {
63 clutter_event_prepare,
64 clutter_event_check,
65 clutter_event_dispatch,
66 NULL
67 };
68
69 /* Special mapping for some keys that don't have a direct Unicode
70 value. Must be sorted by the numeric value of the Windows key
71 virtual key code */
72 static const struct
73 {
74 gushort win_sym, clutter_sym;
75 } clutter_win32_key_map[] =
76 {
77 { VK_CANCEL, CLUTTER_KEY_Cancel },
78 { VK_BACK, CLUTTER_KEY_BackSpace },
79 { VK_TAB, CLUTTER_KEY_Tab },
80 { VK_CLEAR, CLUTTER_KEY_Clear },
81 { VK_RETURN, CLUTTER_KEY_Return },
82 { VK_MENU, CLUTTER_KEY_Menu },
83 { VK_PAUSE, CLUTTER_KEY_Pause },
84 { VK_HANGUL, CLUTTER_KEY_Hangul },
85 { VK_KANJI, CLUTTER_KEY_Kanji },
86 { VK_ESCAPE, CLUTTER_KEY_Escape },
87 { VK_SPACE, CLUTTER_KEY_space },
88 { VK_PRIOR, CLUTTER_KEY_Prior },
89 { VK_NEXT, CLUTTER_KEY_Next },
90 { VK_END, CLUTTER_KEY_End },
91 { VK_HOME, CLUTTER_KEY_Home },
92 { VK_LEFT, CLUTTER_KEY_Left },
93 { VK_UP, CLUTTER_KEY_Up },
94 { VK_RIGHT, CLUTTER_KEY_Right },
95 { VK_DOWN, CLUTTER_KEY_Down },
96 { VK_SELECT, CLUTTER_KEY_Select },
97 { VK_PRINT, CLUTTER_KEY_Print },
98 { VK_EXECUTE, CLUTTER_KEY_Execute },
99 { VK_INSERT, CLUTTER_KEY_Insert },
100 { VK_DELETE, CLUTTER_KEY_Delete },
101 { VK_HELP, CLUTTER_KEY_Help },
102 { VK_MULTIPLY, CLUTTER_KEY_multiply },
103 { VK_F1, CLUTTER_KEY_F1 },
104 { VK_F2, CLUTTER_KEY_F2 },
105 { VK_F3, CLUTTER_KEY_F3 },
106 { VK_F4, CLUTTER_KEY_F4 },
107 { VK_F5, CLUTTER_KEY_F5 },
108 { VK_F6, CLUTTER_KEY_F6 },
109 { VK_F7, CLUTTER_KEY_F7 },
110 { VK_F8, CLUTTER_KEY_F8 },
111 { VK_F9, CLUTTER_KEY_F9 },
112 { VK_F10, CLUTTER_KEY_F10 },
113 { VK_F11, CLUTTER_KEY_F11 },
114 { VK_F12, CLUTTER_KEY_F12 },
115 { VK_F13, CLUTTER_KEY_F13 },
116 { VK_F14, CLUTTER_KEY_F14 },
117 { VK_F15, CLUTTER_KEY_F15 },
118 { VK_F16, CLUTTER_KEY_F16 },
119 { VK_F17, CLUTTER_KEY_F17 },
120 { VK_F18, CLUTTER_KEY_F18 },
121 { VK_F19, CLUTTER_KEY_F19 },
122 { VK_F20, CLUTTER_KEY_F20 },
123 { VK_F21, CLUTTER_KEY_F21 },
124 { VK_F22, CLUTTER_KEY_F22 },
125 { VK_F23, CLUTTER_KEY_F23 },
126 { VK_F24, CLUTTER_KEY_F24 },
127 { VK_LSHIFT, CLUTTER_KEY_Shift_L },
128 { VK_RSHIFT, CLUTTER_KEY_Shift_R },
129 { VK_LCONTROL, CLUTTER_KEY_Control_L },
130 { VK_RCONTROL, CLUTTER_KEY_Control_R }
131 };
132
133 #define CLUTTER_WIN32_KEY_MAP_SIZE (G_N_ELEMENTS (clutter_win32_key_map))
134
135 static GSource *
clutter_event_source_new(ClutterBackend * backend)136 clutter_event_source_new (ClutterBackend *backend)
137 {
138 GSource *source = g_source_new (&event_funcs, sizeof (ClutterEventSource));
139 ClutterEventSource *event_source = (ClutterEventSource *) source;
140
141 event_source->backend = backend;
142
143 g_source_set_name (source, "Clutter Win32 Event Source");
144
145 return source;
146 }
147
148 void
_clutter_backend_win32_events_init(ClutterBackend * backend)149 _clutter_backend_win32_events_init (ClutterBackend *backend)
150 {
151 ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
152 GSource *source;
153 ClutterEventSource *event_source;
154
155 source = backend_win32->event_source = clutter_event_source_new (backend);
156 event_source = (ClutterEventSource *) source;
157 g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS);
158
159 event_source->event_poll_fd.fd = G_WIN32_MSG_HANDLE;
160 event_source->event_poll_fd.events = G_IO_IN;
161
162 g_source_add_poll (source, &event_source->event_poll_fd);
163 g_source_set_can_recurse (source, TRUE);
164 g_source_attach (source, NULL);
165 }
166
167 void
_clutter_backend_win32_events_uninit(ClutterBackend * backend)168 _clutter_backend_win32_events_uninit (ClutterBackend *backend)
169 {
170 ClutterBackendWin32 *backend_win32 = CLUTTER_BACKEND_WIN32 (backend);
171
172 if (backend_win32->event_source)
173 {
174 CLUTTER_NOTE (EVENT, "Destroying the event source");
175
176 g_source_destroy (backend_win32->event_source);
177 g_source_unref (backend_win32->event_source);
178 backend_win32->event_source = NULL;
179 }
180 }
181
182 static gboolean
check_msg_pending()183 check_msg_pending ()
184 {
185 MSG msg;
186
187 return PeekMessageW (&msg, NULL, 0, 0, PM_NOREMOVE) ? TRUE : FALSE;
188 }
189
190 static ClutterModifierType
get_modifier_state(WPARAM wparam)191 get_modifier_state (WPARAM wparam)
192 {
193 ClutterModifierType ret = 0;
194
195 if ((wparam & MK_SHIFT))
196 ret |= CLUTTER_SHIFT_MASK;
197 if ((wparam & MK_CONTROL))
198 ret |= CLUTTER_CONTROL_MASK;
199 if ((wparam & MK_LBUTTON))
200 ret |= CLUTTER_BUTTON1_MASK;
201 if ((wparam & MK_MBUTTON))
202 ret |= CLUTTER_BUTTON2_MASK;
203 if ((wparam & MK_RBUTTON))
204 ret |= CLUTTER_BUTTON3_MASK;
205
206 return ret;
207 }
208
209 static inline void
take_and_queue_event(ClutterEvent * event)210 take_and_queue_event (ClutterEvent *event)
211 {
212 /* The event is added directly to the queue instead of using
213 clutter_event_put so that it can avoid a copy. This takes
214 ownership of the event */
215 _clutter_event_push (event, FALSE);
216 }
217
218 static inline void
make_button_event(const MSG * msg,ClutterStage * stage,int button,int click_count,gboolean release,ClutterInputDevice * device)219 make_button_event (const MSG *msg,
220 ClutterStage *stage,
221 int button,
222 int click_count,
223 gboolean release,
224 ClutterInputDevice *device)
225 {
226 ClutterEvent *event = clutter_event_new (release ?
227 CLUTTER_BUTTON_RELEASE :
228 CLUTTER_BUTTON_PRESS);
229
230 event->any.stage = stage;
231
232 event->button.time = msg->time;
233 event->button.x = GET_X_LPARAM (msg->lParam);
234 event->button.y = GET_Y_LPARAM (msg->lParam);
235 event->button.modifier_state = get_modifier_state (msg->wParam);
236 event->button.button = button;
237 event->button.click_count = click_count;
238 clutter_event_set_device (event, device);
239
240 take_and_queue_event (event);
241 }
242
243 static gboolean
clutter_event_prepare(GSource * source,gint * timeout)244 clutter_event_prepare (GSource *source,
245 gint *timeout)
246 {
247 gboolean retval;
248
249 _clutter_threads_acquire_lock ();
250
251 *timeout = -1;
252 retval = (clutter_events_pending () || check_msg_pending ());
253
254 _clutter_threads_release_lock ();
255
256 return retval;
257 }
258
259 static gboolean
clutter_event_check(GSource * source)260 clutter_event_check (GSource *source)
261 {
262 ClutterEventSource *event_source = (ClutterEventSource *) source;
263 gboolean retval;
264
265 _clutter_threads_acquire_lock ();
266
267 if ((event_source->event_poll_fd.revents & G_IO_IN))
268 retval = (clutter_events_pending () || check_msg_pending ());
269 else
270 retval = FALSE;
271
272 _clutter_threads_release_lock ();
273
274 return retval;
275 }
276
277 static gboolean
clutter_event_dispatch(GSource * source,GSourceFunc callback,gpointer user_data)278 clutter_event_dispatch (GSource *source,
279 GSourceFunc callback,
280 gpointer user_data)
281 {
282 ClutterEvent *event;
283 MSG msg;
284
285 _clutter_threads_acquire_lock ();
286
287 /* Process Windows messages until we've got one that translates into
288 the clutter event queue */
289 while (!clutter_events_pending () && PeekMessageW (&msg, NULL,
290 0, 0, PM_REMOVE))
291 DispatchMessageW (&msg);
292
293 /* Pop an event off the queue if any */
294 if ((event = clutter_event_get ()))
295 {
296 /* forward the event into clutter for emission etc. */
297 if (event->any.stage)
298 _clutter_stage_queue_event (event->any.stage, event, FALSE);
299 }
300
301 _clutter_threads_release_lock ();
302
303 return TRUE;
304 }
305
306 static ClutterModifierType
get_key_modifier_state(const BYTE * key_states)307 get_key_modifier_state (const BYTE *key_states)
308 {
309 ClutterModifierType ret = 0;
310
311 if ((key_states[VK_SHIFT] & 0x80)
312 || (key_states[VK_LSHIFT] & 0x80)
313 || (key_states[VK_RSHIFT] & 0x80))
314 ret |= CLUTTER_SHIFT_MASK;
315 if ((key_states[VK_CONTROL] & 0x80)
316 || (key_states[VK_LCONTROL] & 0x80)
317 || (key_states[VK_RCONTROL] & 0x80))
318 ret |= CLUTTER_CONTROL_MASK;
319 if ((key_states[VK_MENU] & 0x80)
320 || (key_states[VK_LMENU] & 0x80)
321 || (key_states[VK_RMENU] & 0x80))
322 ret |= CLUTTER_MOD1_MASK;
323 if (key_states[VK_CAPITAL])
324 ret |= CLUTTER_LOCK_MASK;
325
326 return ret;
327 }
328
329 /**
330 * clutter_win32_handle_event:
331 * @msg: A pointer to a structure describing a Win32 message.
332 *
333 * This function processes a single Win32 message. It can be used to
334 * hook into external windows message processing (for example, a GDK
335 * filter function).
336 *
337 * If clutter_win32_disable_event_retrieval() has been called, you must
338 * let this function process events to update Clutter's internal state.
339 *
340 * Return value: %TRUE if the message was handled entirely by Clutter
341 * and no further processing (such as calling the default window
342 * procedure) should take place. %FALSE is returned if is the message
343 * was not handled at all or if Clutter expects processing to take
344 * place.
345 *
346 * Since: 1.6
347 */
348 gboolean
clutter_win32_handle_event(const MSG * msg)349 clutter_win32_handle_event (const MSG *msg)
350 {
351 ClutterBackend *backend;
352 ClutterBackendWin32 *backend_win32;
353 ClutterStageWin32 *stage_win32;
354 ClutterDeviceManager *manager;
355 ClutterInputDevice *core_pointer, *core_keyboard;
356 ClutterStage *stage;
357 ClutterStageWindow *impl;
358 gboolean return_value = FALSE;
359
360 stage = clutter_win32_get_stage_from_window (msg->hwnd);
361
362 /* Ignore any messages for windows which we don't have a stage for */
363 if (stage == NULL)
364 return FALSE;
365
366 impl = _clutter_stage_get_window (stage);
367 stage_win32 = CLUTTER_STAGE_WIN32 (impl);
368 backend_win32 = stage_win32->backend;
369 backend = CLUTTER_BACKEND (backend_win32);
370
371 /* Give Cogl a chance to handle the message first */
372 if (backend->cogl_renderer != NULL &&
373 cogl_win32_renderer_handle_event (backend->cogl_renderer,
374 (void *) msg) == COGL_FILTER_REMOVE)
375 return TRUE;
376
377 manager = clutter_device_manager_get_default ();
378 if (manager == NULL)
379 return FALSE;
380
381 core_pointer =
382 clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE);
383 core_keyboard =
384 clutter_device_manager_get_core_device (manager, CLUTTER_KEYBOARD_DEVICE);
385
386 switch (msg->message)
387 {
388 case WM_SIZE:
389 if (!stage_win32->is_foreign_win
390 /* Ignore size changes resulting from the stage being
391 minimized - otherwise the window size will be set to
392 0,0 */
393 && msg->wParam != SIZE_MINIMIZED)
394 {
395 WORD new_width = LOWORD (msg->lParam);
396 WORD new_height = HIWORD (msg->lParam);
397 gfloat old_width, old_height;
398
399 clutter_actor_get_size (CLUTTER_ACTOR (stage),
400 &old_width, &old_height);
401
402 if (new_width != old_width || new_height != old_height)
403 clutter_actor_set_size (CLUTTER_ACTOR (stage),
404 new_width, new_height);
405 }
406 break;
407
408 case WM_SHOWWINDOW:
409 if (msg->wParam)
410 clutter_stage_win32_map (stage_win32);
411 else
412 clutter_stage_win32_unmap (stage_win32);
413 break;
414
415 case WM_ACTIVATE:
416 if (msg->wParam == WA_INACTIVE)
417 {
418 if (_clutter_stage_is_activated (stage_win32->wrapper))
419 {
420 _clutter_stage_update_state (stage_win32->wrapper,
421 CLUTTER_STAGE_STATE_ACTIVATED,
422 0);
423 }
424 }
425 else if (!_clutter_stage_is_activated (stage_win32->wrapper))
426 {
427 _clutter_stage_update_state (stage_win32->wrapper,
428 0,
429 CLUTTER_STAGE_STATE_ACTIVATED);
430 }
431 break;
432
433 case WM_PAINT:
434 CLUTTER_NOTE (BACKEND, "expose for stage:%p, redrawing", stage);
435 clutter_stage_ensure_redraw (stage);
436 break;
437
438 case WM_DESTROY:
439 {
440 ClutterEvent *event = clutter_event_new (CLUTTER_DESTROY_NOTIFY);
441
442 CLUTTER_NOTE (EVENT, "WM_DESTROY");
443
444 event->any.stage = stage;
445
446 take_and_queue_event (event);
447 }
448 break;
449
450 case WM_CLOSE:
451 {
452 ClutterEvent *event = clutter_event_new (CLUTTER_DELETE);
453
454 CLUTTER_NOTE (EVENT, "WM_CLOSE");
455
456 event->any.stage = stage;
457
458 take_and_queue_event (event);
459
460 /* The default window proc will destroy the window so we want to
461 prevent this to allow applications to optionally destroy the
462 window themselves */
463 return_value = TRUE;
464 }
465 break;
466
467 case WM_LBUTTONDOWN:
468 make_button_event (msg, stage, 1, 1, FALSE, core_pointer);
469 break;
470
471 case WM_MBUTTONDOWN:
472 make_button_event (msg, stage, 2, 1, FALSE, core_pointer);
473 break;
474
475 case WM_RBUTTONDOWN:
476 make_button_event (msg, stage, 3, 1, FALSE, core_pointer);
477 break;
478
479 case WM_LBUTTONUP:
480 make_button_event (msg, stage, 1, 1, TRUE, core_pointer);
481 break;
482
483 case WM_MBUTTONUP:
484 make_button_event (msg, stage, 2, 1, TRUE, core_pointer);
485 break;
486
487 case WM_RBUTTONUP:
488 make_button_event (msg, stage, 3, 1, TRUE, core_pointer);
489 break;
490
491 case WM_LBUTTONDBLCLK:
492 make_button_event (msg, stage, 1, 2, FALSE, core_pointer);
493 break;
494
495 case WM_MBUTTONDBLCLK:
496 make_button_event (msg, stage, 2, 2, FALSE, core_pointer);
497 break;
498
499 case WM_RBUTTONDBLCLK:
500 make_button_event (msg, stage, 3, 2, FALSE, core_pointer);
501 break;
502
503 case WM_MOUSEWHEEL:
504 stage_win32->scroll_pos += (SHORT) HIWORD (msg->wParam);
505
506 while (abs (stage_win32->scroll_pos) >= WHEEL_DELTA)
507 {
508 ClutterEvent *event = clutter_event_new (CLUTTER_SCROLL);
509 POINT pt;
510
511 event->scroll.time = msg->time;
512 event->scroll.modifier_state =
513 get_modifier_state (LOWORD (msg->wParam));
514 event->any.stage = stage;
515
516 clutter_event_set_device (event, core_pointer);
517
518 /* conversion to window coordinates is required */
519 pt.x = GET_X_LPARAM (msg->lParam);
520 pt.y = GET_Y_LPARAM (msg->lParam);
521 ScreenToClient (msg->hwnd, &pt);
522 event->scroll.x = pt.x;
523 event->scroll.y = pt.y;
524
525 if (stage_win32->scroll_pos > 0)
526 {
527 event->scroll.direction = CLUTTER_SCROLL_UP;
528 stage_win32->scroll_pos -= WHEEL_DELTA;
529 }
530 else
531 {
532 event->scroll.direction = CLUTTER_SCROLL_DOWN;
533 stage_win32->scroll_pos += WHEEL_DELTA;
534 }
535
536 take_and_queue_event (event);
537 }
538 break;
539
540 case WM_MOUSEMOVE:
541 {
542 ClutterEvent *event = clutter_event_new (CLUTTER_MOTION);
543
544 event->motion.time = msg->time;
545 event->motion.x = GET_X_LPARAM (msg->lParam);
546 event->motion.y = GET_Y_LPARAM (msg->lParam);
547 event->motion.modifier_state = get_modifier_state (msg->wParam);
548 event->any.stage = stage;
549
550 clutter_event_set_device (event, core_pointer);
551
552 /* We need to start tracking when the mouse enters the stage if
553 we're not already */
554 if (!stage_win32->tracking_mouse)
555 {
556 ClutterEvent *crossing = clutter_event_new (CLUTTER_ENTER);
557 TRACKMOUSEEVENT tmevent;
558
559 tmevent.cbSize = sizeof (tmevent);
560 tmevent.dwFlags = TME_LEAVE;
561 tmevent.hwndTrack = stage_win32->hwnd;
562 TrackMouseEvent (&tmevent);
563
564 event->crossing.time = msg->time;
565 event->crossing.x = event->motion.x;
566 event->crossing.y = event->motion.y;
567 event->crossing.stage = stage;
568 event->crossing.source = CLUTTER_ACTOR (stage);
569 event->crossing.related = NULL;
570
571 clutter_event_set_device (event, core_pointer);
572
573 /* we entered the stage */
574 _clutter_input_device_set_stage (core_pointer, stage);
575
576 take_and_queue_event (crossing);
577
578 stage_win32->tracking_mouse = TRUE;
579 }
580
581 take_and_queue_event (event);
582 }
583 break;
584
585 case WM_MOUSELEAVE:
586 {
587 ClutterEvent *event = clutter_event_new (CLUTTER_LEAVE);
588
589 event->crossing.time = msg->time;
590 event->crossing.x = msg->pt.x;
591 event->crossing.y = msg->pt.y;
592 event->crossing.stage = stage;
593 event->crossing.source = CLUTTER_ACTOR (stage);
594 event->crossing.related = NULL;
595
596 clutter_event_set_device (event, core_pointer);
597
598 /* we left the stage */
599 _clutter_input_device_set_stage (core_pointer, NULL);
600
601 /* When we get a leave message the mouse tracking is
602 automatically cancelled so we'll need to start it again when
603 the mouse next enters the window */
604 stage_win32->tracking_mouse = FALSE;
605
606 take_and_queue_event (event);
607 }
608 break;
609
610 case WM_KEYDOWN:
611 case WM_KEYUP:
612 case WM_SYSKEYDOWN:
613 case WM_SYSKEYUP:
614 {
615 ClutterEvent *event = clutter_event_new (CLUTTER_EVENT_NONE);
616 int scan_code = (msg->lParam >> 16) & 0xff;
617 int min = 0, max = CLUTTER_WIN32_KEY_MAP_SIZE, mid;
618 BYTE key_states[256];
619
620 /* Get the keyboard modifier states. GetKeyboardState
621 conveniently gets the key state that was current when the
622 last keyboard message read was generated */
623 GetKeyboardState(key_states);
624
625 /* Binary chop to check if we have a direct mapping for this
626 key code */
627 while (min < max)
628 {
629 mid = (min + max) / 2;
630 if (clutter_win32_key_map[mid].win_sym == msg->wParam)
631 {
632 event->key.keyval = clutter_win32_key_map[mid].clutter_sym;
633 event->key.unicode_value = 0;
634 break;
635 }
636 else if (clutter_win32_key_map[mid].win_sym < msg->wParam)
637 min = mid + 1;
638 else
639 max = mid;
640 }
641
642 /* If we don't have a direct mapping then try getting the
643 unicode value of the key sym */
644 if (min >= max)
645 {
646 WCHAR ch;
647 BYTE shift_state[256];
648
649 /* Translate to a Unicode value, but only take into
650 account the shift key. That way Ctrl+Shift+C will
651 generate a capital C virtual key code with a zero
652 unicode value for example */
653 memset (shift_state, 0, 256);
654 shift_state[VK_SHIFT] = key_states[VK_SHIFT];
655 shift_state[VK_LSHIFT] = key_states[VK_LSHIFT];
656 shift_state[VK_RSHIFT] = key_states[VK_RSHIFT];
657 shift_state[VK_CAPITAL] = key_states[VK_CAPITAL];
658
659 if (ToUnicode (msg->wParam, scan_code,
660 shift_state, &ch, 1, 0) == 1
661 /* The codes in this range directly match the Latin 1
662 codes so we can just use the Unicode value as the
663 key sym */
664 && ch >= 0x20 && ch <= 0xff)
665 event->key.keyval = ch;
666 else
667 /* Otherwise we don't know what the key means but the
668 application might be able to do something with the
669 scan code so we might as well still generate the
670 event */
671 event->key.keyval = CLUTTER_KEY_VoidSymbol;
672
673 /* Get the unicode value of the keypress again using the
674 full modifier state */
675 if (ToUnicode (msg->wParam, scan_code,
676 key_states, &ch, 1, 0) == 1)
677 event->key.unicode_value = ch;
678 else
679 event->key.unicode_value = 0;
680 }
681
682 event->key.type = msg->message == WM_KEYDOWN
683 || msg->message == WM_SYSKEYDOWN
684 ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE;
685 event->key.time = msg->time;
686 event->key.modifier_state = get_key_modifier_state (key_states);
687 event->key.hardware_keycode = scan_code;
688 event->any.stage = stage;
689
690 clutter_event_set_device (event, core_keyboard);
691
692 take_and_queue_event (event);
693 }
694 break;
695
696 case WM_GETMINMAXINFO:
697 {
698 MINMAXINFO *min_max_info = (MINMAXINFO *) msg->lParam;
699 _clutter_stage_win32_get_min_max_info (stage_win32, min_max_info);
700 return_value = TRUE;
701 }
702 break;
703
704 case WM_SETCURSOR:
705 /* If the cursor is in the window's client area and the stage's
706 cursor should be invisible then we'll set a blank cursor
707 instead */
708 if (LOWORD (msg->lParam) == HTCLIENT && !stage_win32->is_cursor_visible)
709 {
710 return_value = TRUE;
711 _clutter_stage_win32_update_cursor (stage_win32);
712 }
713 break;
714 }
715
716 return return_value;
717 }
718
719 LRESULT CALLBACK
_clutter_stage_win32_window_proc(HWND hwnd,UINT umsg,WPARAM wparam,LPARAM lparam)720 _clutter_stage_win32_window_proc (HWND hwnd, UINT umsg,
721 WPARAM wparam, LPARAM lparam)
722 {
723 gboolean message_handled = FALSE;
724
725 /* Windows sends some messages such as WM_GETMINMAXINFO and
726 WM_CREATE before returning from CreateWindow. The stage point
727 will not yet be stored in the Window structure in this case so we
728 need to skip these messages because we can't know which stage the
729 window belongs to */
730 if (GetWindowLongPtrW (hwnd, 0))
731 {
732 MSG msg;
733 DWORD message_pos = GetMessagePos ();
734
735 /* Convert the parameters to a MSG struct */
736 msg.hwnd = hwnd;
737 msg.message = umsg;
738 msg.wParam = wparam;
739 msg.lParam = lparam;
740 msg.time = GetMessageTime ();
741 /* Neither MAKE_POINTS nor GET_[XY]_LPARAM is defined in MinGW
742 headers so we need to convert to a signed type explicitly */
743 msg.pt.x = (SHORT) LOWORD (message_pos);
744 msg.pt.y = (SHORT) HIWORD (message_pos);
745
746 /* Process the message */
747 message_handled = clutter_win32_handle_event (&msg);
748 }
749
750 if (!message_handled)
751 return DefWindowProcW (hwnd, umsg, wparam, lparam);
752 else
753 return 0;
754 }
755