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