comparison src/msw-proc.c @ 213:78f53ef88e17 r20-4b5

Import from CVS: tag r20-4b5
author cvs
date Mon, 13 Aug 2007 10:06:47 +0200
parents
children d44af0c54775
comparison
equal deleted inserted replaced
212:d8688acf4c5b 213:78f53ef88e17
1 /* mswindows specific event-handling.
2 Copyright (C) 1997 Jonathan Harris.
3
4 This file is part of XEmacs.
5
6 XEmacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 XEmacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with XEmacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21 /* Synched up with: Not in FSF. */
22
23 /* Authorship:
24
25 Jonathan Harris, November 1997 for 20.4.
26 */
27
28 /*
29 * Comment:
30 *
31 * Windows user-input type events are stored in a per-thread message queue
32 * and retrieved using GetMessage(). It is not possible to wait on this
33 * queue and on other events (eg process input) simultaneously. Also, the
34 * main event-handling code in windows (the "windows procedure") is called
35 * asynchronously when windows has certain other types of events ("nonqueued
36 * messages") to deliver. The documentation doesn't appear to specify the
37 * context in which the windows procedure is called, but I assume that the
38 * thread that created the window is temporarily highjacked for this purpose.
39 *
40 * We spawn off a single thread to deal with both kinds of messages. The
41 * thread turns the windows events into emacs_events and stuffs them in a
42 * queue which XEmacs reads at its leisure. This file contains the code for
43 * the thread. This scheme also helps to prevent weird synchronisation and
44 * deadlock problems that might occur if the windows procedure was called
45 * when XEmacs was already in the middle of processing an event.
46 *
47 * Unfortunately, only the thread that created a window can retrieve messages
48 * destined for that window ("GetMessage does not retrieve messages for
49 * windows that belong to other threads..."). This means that our message-
50 * processing thread also has to do all window creation. We handle this
51 * bogosity by getting the main XEmacs thread to send special user-defined
52 * messages to the message-processing thread to instruct it to create windows.
53 */
54
55
56 #include <config.h>
57 #include "lisp.h"
58
59 #include "console-msw.h"
60 #include "device.h"
61 #include "frame.h"
62 #include "events.h"
63 #include "event-msw.h"
64
65 #define MSWINDOWS_FRAME_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TILEDWINDOW
66 #define MSWINDOWS_POPUP_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_CAPTION|WS_POPUP
67
68 static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
69 static Lisp_Object mswindows_find_console (HWND hwnd);
70 static Lisp_Object mswindows_find_frame (HWND hwnd);
71 static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key);
72
73 /*
74 * Entry point for the "windows" message-processing thread
75 */
76 DWORD mswindows_win_thread()
77 {
78 WNDCLASS wc;
79 MSG msg;
80 mswindows_waitable_info_type info;
81
82 /* Register the main window class */
83 wc.style = CS_OWNDC; /* One DC per window */
84 wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc;
85 wc.cbClsExtra = 0;
86 wc.cbWndExtra = 0; /* ? */
87 wc.hInstance = NULL; /* ? */
88 wc.hIcon = LoadIcon (NULL, XEMACS_CLASS);
89 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
90 wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH); */
91 wc.lpszMenuName = NULL; /* XXX FIXME? Add a menu? */
92 wc.lpszClassName = XEMACS_CLASS;
93 RegisterClass(&wc); /* XXX FIXME: Should use RegisterClassEx */
94
95 info.type = mswindows_waitable_type_dispatch;
96 mswindows_add_waitable(&info);
97
98 /* Ensure our message queue is created XXX FIXME: Is this necessary? */
99 PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE);
100
101 /* Notify the main thread that we're ready */
102 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0));
103
104 /* Main windows loop */
105 while (1)
106 {
107 GetMessage (&msg, NULL, 0, 0);
108
109 /*
110 * Process things that don't have an associated window, so wouldn't
111 * get sent to mswindows_wnd_proc
112 */
113
114 /* Request from main thread */
115 if (msg.message>=WM_XEMACS_BASE && msg.message<=WM_XEMACS_END)
116 mswindows_handle_request(&msg);
117
118 /* Timeout */
119 else if (msg.message == WM_TIMER)
120 {
121 Lisp_Object emacs_event;
122 struct Lisp_Event *event;
123
124 KillTimer(NULL, msg.wParam);
125 EnterCriticalSection (&mswindows_dispatch_crit);
126 emacs_event = Fmake_event (Qnil, Qnil);
127 event = XEVENT(emacs_event);
128
129 event->channel = Qnil;
130 event->timestamp = msg.time;
131 event->event_type = timeout_event;
132 event->event.timeout.interval_id = msg.wParam;
133 mswindows_enqueue_dispatch_event (emacs_event);
134 LeaveCriticalSection (&mswindows_dispatch_crit);
135 }
136 else
137 /* Pass on to mswindows_wnd_proc */
138 DispatchMessage (&msg);
139 }
140 }
141
142 /*
143 * The windows procedure for the window class XEMACS_CLASS
144 * Stuffs messages in the mswindows event queue
145 */
146 static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam,
147 LPARAM lParam)
148 {
149 /* Note: Remember to initialise these before use */
150 Lisp_Object emacs_event;
151 struct Lisp_Event *event;
152
153 static int mods = 0;
154 MSG msg = { hwnd, message, wParam, lParam, 0, {0,0} };
155 msg.time = GetMessageTime();
156
157 #if 0 /* XXX */
158 stderr_out("Message %04x, wParam=%04x, lParam=%08lx\n", message, wParam, lParam);
159 #endif
160 switch (message)
161 {
162 case WM_KEYDOWN:
163 case WM_SYSKEYDOWN:
164 switch(wParam)
165 {
166 case VK_SHIFT:
167 mods |= MOD_SHIFT;
168 break;
169 case VK_CONTROL:
170 mods |= MOD_CONTROL;
171 break;
172 case VK_MENU:
173 mods |= MOD_META;
174 break;
175 default:
176 /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */
177 {
178 Lisp_Object keysym;
179 if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam)))
180 {
181 EnterCriticalSection (&mswindows_dispatch_crit);
182 emacs_event = Fmake_event (Qnil, Qnil);
183 event = XEVENT(emacs_event);
184
185 event->channel = mswindows_find_console(hwnd);
186 event->timestamp = msg.time;
187 event->event_type = key_press_event;
188 event->event.key.keysym = keysym;
189 event->event.key.modifiers = mods;
190 mswindows_enqueue_dispatch_event (emacs_event);
191 LeaveCriticalSection (&mswindows_dispatch_crit);
192 return (0);
193 }
194 }
195 }
196 TranslateMessage (&msg); /* Maybe generates WM_[SYS]CHAR in message queue */
197 goto defproc;
198
199 case WM_KEYUP:
200 case WM_SYSKEYUP:
201 switch(wParam)
202 {
203 case VK_SHIFT:
204 mods &= ~MOD_SHIFT;
205 break;
206 case VK_CONTROL:
207 mods &= ~MOD_CONTROL;
208 break;
209 case VK_MENU:
210 mods &= ~MOD_META;
211 break;
212 }
213 TranslateMessage (&msg);
214 goto defproc;
215
216 case WM_CHAR:
217 case WM_SYSCHAR:
218 {
219 EnterCriticalSection (&mswindows_dispatch_crit);
220 emacs_event = Fmake_event (Qnil, Qnil);
221 event = XEVENT(emacs_event);
222
223 event->channel = mswindows_find_console(hwnd);
224 event->timestamp = msg.time;
225 event->event_type = key_press_event;
226 event->event.key.modifiers = mods;
227 event->event.key.modifiers = lParam & 0x20000000 ? MOD_META : 0; /* redundant? */
228 if (wParam<' ') /* Control char not handled under WM_KEYDOWN */
229 {
230 event->event.key.keysym = make_char(wParam+'a'-1);
231 event->event.key.modifiers |= MOD_CONTROL; /* redundant? */
232 }
233 else
234 {
235 /* Assumes that emacs keysym == ASCII code */
236 event->event.key.keysym = make_char(wParam);
237 }
238 mswindows_enqueue_dispatch_event (emacs_event);
239 LeaveCriticalSection (&mswindows_dispatch_crit);
240 }
241 break;
242
243 case WM_LBUTTONDOWN:
244 case WM_MBUTTONDOWN:
245 case WM_RBUTTONDOWN:
246 case WM_LBUTTONUP:
247 case WM_MBUTTONUP:
248 case WM_RBUTTONUP:
249 {
250 /* XXX FIXME: Do middle button emulation */
251 EnterCriticalSection (&mswindows_dispatch_crit);
252 emacs_event = Fmake_event (Qnil, Qnil);
253 event = XEVENT(emacs_event);
254
255 event->channel = mswindows_find_frame(hwnd);
256 event->timestamp = msg.time;
257 event->event_type =
258 (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
259 message==WM_RBUTTONDOWN) ?
260 button_press_event : button_release_event;
261 #if 0
262 ((wParam & MK_CONTROL) ? MOD_CONTROL : 0) |
263 ((wParam & MK_SHIFT) ? MOD_SHIFT : 0);
264 #endif
265 event->event.button.button =
266 (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
267 ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2);
268 event->event.button.x = LOWORD(lParam);
269 event->event.button.y = HIWORD(lParam);
270 event->event.button.modifiers = mods;
271
272 mswindows_enqueue_dispatch_event (emacs_event);
273 LeaveCriticalSection (&mswindows_dispatch_crit);
274 }
275 break;
276
277 case WM_MOUSEMOVE:
278 {
279 EnterCriticalSection (&mswindows_dispatch_crit);
280 emacs_event = Fmake_event (Qnil, Qnil);
281 event = XEVENT(emacs_event);
282
283 event->channel = mswindows_find_frame(hwnd);
284 event->timestamp = msg.time;
285 event->event_type = pointer_motion_event;
286 event->event.motion.x = LOWORD(lParam);
287 event->event.motion.y = HIWORD(lParam);
288 event->event.motion.modifiers = mods;
289
290 mswindows_enqueue_dispatch_event (emacs_event);
291 LeaveCriticalSection (&mswindows_dispatch_crit);
292 }
293 break;
294
295 case WM_PAINT:
296 if (GetUpdateRect(hwnd, NULL, FALSE))
297 {
298 PAINTSTRUCT paintStruct;
299
300 EnterCriticalSection (&mswindows_dispatch_crit);
301 emacs_event = Fmake_event (Qnil, Qnil);
302 event = XEVENT(emacs_event);
303
304 event->channel = mswindows_find_frame(hwnd);
305 event->timestamp = msg.time;
306 event->event_type = magic_event;
307 BeginPaint (hwnd, &paintStruct);
308 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message;
309 EVENT_MSWINDOWS_MAGIC_DATA(event) = paintStruct.rcPaint;
310 EndPaint (hwnd, &paintStruct);
311
312 mswindows_enqueue_dispatch_event (emacs_event);
313 LeaveCriticalSection (&mswindows_dispatch_crit);
314 }
315 break;
316
317 case WM_SIZE:
318 /* We only care about this message if our size has really changed */
319 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
320 {
321 RECT rect;
322 EnterCriticalSection (&mswindows_dispatch_crit);
323 emacs_event = Fmake_event (Qnil, Qnil);
324 event = XEVENT(emacs_event);
325
326 event->channel = mswindows_find_frame(hwnd);
327 event->timestamp = msg.time;
328 event->event_type = magic_event;
329 if (wParam==SIZE_MINIMIZED)
330 rect.left = rect.top = rect.right = rect.bottom = -1;
331 else
332 GetClientRect(hwnd, &rect);
333 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message;
334 EVENT_MSWINDOWS_MAGIC_DATA(event) = rect;
335
336 mswindows_enqueue_dispatch_event (emacs_event);
337 LeaveCriticalSection (&mswindows_dispatch_crit);
338 }
339 break;
340
341 case WM_SETFOCUS:
342 case WM_KILLFOCUS:
343 {
344 EnterCriticalSection (&mswindows_dispatch_crit);
345 emacs_event = Fmake_event (Qnil, Qnil);
346 event = XEVENT(emacs_event);
347
348 event->channel = mswindows_find_frame(hwnd);
349 event->timestamp = msg.time;
350 event->event_type = magic_event;
351 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message;
352
353 mswindows_enqueue_dispatch_event (emacs_event);
354 LeaveCriticalSection (&mswindows_dispatch_crit);
355 }
356 break;
357
358 case WM_QUIT:
359 /* XXX FIXME: Should do something here! */
360 defproc:
361 default:
362 return DefWindowProc (hwnd, message, wParam, lParam);
363 }
364 return (0);
365 }
366
367
368 /*
369 * Make a request to the message-processing thread to do things that
370 * can't be done in the main thread.
371 */
372 LPARAM
373 mswindows_make_request(UINT message, WPARAM wParam, mswindows_request_type *request)
374 {
375 MSG msg;
376 assert(PostThreadMessage (mswindows_win_thread_id, message, wParam,
377 (LPARAM) request));
378 GetMessage (&msg, NULL, WM_XEMACS_ACK, WM_XEMACS_ACK);
379 return (msg.lParam);
380 }
381
382
383 /*
384 * Handle a request from the main thread to do things that have to be
385 * done in the message-processing thread.
386 */
387 static void
388 mswindows_handle_request (MSG *msg)
389 {
390 mswindows_request_type *request = (mswindows_request_type *) msg->lParam;
391
392 switch (msg->message)
393 {
394 case WM_XEMACS_CREATEWINDOW:
395 {
396 struct frame *f = request->thing1;
397 Lisp_Object *props = request->thing2;
398 Lisp_Object name, height, width, popup, top, left;
399 RECT rect;
400 DWORD style;
401 HWND hwnd;
402
403 name = Fplist_get (*props, Qname, Qnil);
404 height = Fplist_get (*props, Qheight, Qnil);
405 width = Fplist_get (*props, Qwidth, Qnil);
406 popup = Fplist_get (*props, Qpopup, Qnil);
407 top = Fplist_get (*props, Qtop, Qnil);
408 left = Fplist_get (*props, Qleft, Qnil);
409
410 style = (NILP(popup)) ? MSWINDOWS_FRAME_STYLE : MSWINDOWS_POPUP_STYLE;
411
412 rect.left = rect.top = 0;
413 rect.right = INTP(width) ? XINT(width) : 640;
414 rect.bottom = INTP(height) ? XINT(height) : 480;
415 #ifdef HAVE_MENUBARS
416 AdjustWindowRect(&rect, style, TRUE);
417 #else
418 AdjustWindowRect(&rect, style, FALSE);
419 #endif
420
421 hwnd = CreateWindow (XEMACS_CLASS,
422 STRINGP(f->name) ? XSTRING_DATA(f->name) :
423 (STRINGP(name) ? XSTRING_DATA(name) : XEMACS_CLASS),
424 style,
425 INTP(left) ? XINT(left) : CW_USEDEFAULT,
426 INTP(top) ? XINT(top) : CW_USEDEFAULT,
427 rect.right-rect.left, rect.bottom-rect.top,
428 NULL, NULL, NULL, NULL);
429 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, (LPARAM) hwnd));
430 }
431 return;
432
433 case WM_XEMACS_SETTIMER:
434 {
435 UINT id;
436 id=SetTimer (NULL, 0, (UINT) request->thing1, NULL);
437 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, id));
438 }
439 break;
440
441 case WM_XEMACS_KILLTIMER:
442 {
443 KillTimer (NULL, (UINT) request->thing1);
444 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0));
445 }
446 break;
447
448 default:
449 assert(0);
450 }
451 }
452
453
454 /*
455 * Translate a mswindows virtual key to a keysym.
456 * Only returns non-Qnil for keys that don't generate WM_CHAR messages
457 * or whose ASCII codes (like space) xemacs doesn't like.
458 * Virtual key values are defined in winresrc.h
459 * XXX I'm not sure that KEYSYM("name") is the best thing to use here.
460 */
461 Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key)
462 {
463 switch (mswindows_key)
464 {
465 /* First the predefined ones */
466 case VK_BACK: return QKbackspace;
467 case VK_TAB: return QKtab;
468 case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */
469 case VK_RETURN: return QKreturn;
470 case VK_ESCAPE: return QKescape;
471 case VK_SPACE: return QKspace;
472 case VK_DELETE: return QKdelete;
473
474 /* The rest */
475 case VK_PRIOR: return KEYSYM ("prior");
476 case VK_NEXT: return KEYSYM ("next");
477 case VK_END: return KEYSYM ("end");
478 case VK_HOME: return KEYSYM ("home");
479 case VK_LEFT: return KEYSYM ("left");
480 case VK_UP: return KEYSYM ("up");
481 case VK_RIGHT: return KEYSYM ("right");
482 case VK_DOWN: return KEYSYM ("down");
483 case VK_INSERT: return KEYSYM ("insert");
484 case VK_HELP: return KEYSYM ("help");
485 case VK_F1: return KEYSYM ("F1");
486 case VK_F2: return KEYSYM ("F2");
487 case VK_F3: return KEYSYM ("F3");
488 case VK_F4: return KEYSYM ("F4");
489 case VK_F5: return KEYSYM ("F5");
490 case VK_F6: return KEYSYM ("F6");
491 case VK_F7: return KEYSYM ("F7");
492 case VK_F8: return KEYSYM ("F8");
493 case VK_F9: return KEYSYM ("F9");
494 case VK_F10: return KEYSYM ("F10");
495 case VK_F11: return KEYSYM ("F11");
496 case VK_F12: return KEYSYM ("F12");
497 case VK_F13: return KEYSYM ("F13");
498 case VK_F14: return KEYSYM ("F14");
499 case VK_F15: return KEYSYM ("F15");
500 case VK_F16: return KEYSYM ("F16");
501 case VK_F17: return KEYSYM ("F17");
502 case VK_F18: return KEYSYM ("F18");
503 case VK_F19: return KEYSYM ("F19");
504 case VK_F20: return KEYSYM ("F20");
505 case VK_F21: return KEYSYM ("F21");
506 case VK_F22: return KEYSYM ("F22");
507 case VK_F23: return KEYSYM ("F23");
508 case VK_F24: return KEYSYM ("F24");
509 }
510 return Qnil;
511 }
512
513
514 /*
515 * Find the console that matches the supplied mswindows window handle
516 */
517 static Lisp_Object
518 mswindows_find_console (HWND hwnd)
519 {
520 Lisp_Object concons;
521
522 CONSOLE_LOOP (concons)
523 {
524 Lisp_Object console = XCAR (concons);
525 /* We only support one console so this must be it */
526 return console;
527 }
528
529 return Qnil;
530 }
531
532 /*
533 * Find the frame that matches the supplied mswindows window handle
534 */
535 static Lisp_Object
536 mswindows_find_frame (HWND hwnd)
537 {
538 Lisp_Object frmcons, devcons, concons;
539
540 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
541 {
542 struct frame *f;
543 Lisp_Object frame = XCAR (frmcons);
544 f = XFRAME (frame);
545 if (FRAME_TYPE_P(f, mswindows)) /* Might be a stream-type frame */
546 if (FRAME_MSWINDOWS_HANDLE(f)==hwnd)
547 return frame;
548 }
549 assert(0); /* XXX Can't happen! we only get messages for our windows */
550 return Qnil;
551 }
552
553 /*
554 * Random helper functions for debugging.
555 * Intended for use in the MSVC "Watch" window which doesn't like
556 * the aborts that the error_check_foo() functions can make.
557 */
558 struct lrecord_header *DHEADER(Lisp_Object obj)
559 {
560 return LRECORDP(obj) ? XRECORD_LHEADER(obj) : NULL;
561 /* (lrecord_header*)(obj & 0xfffffff) */
562 }
563
564 struct Lisp_Event *DEVENT(Lisp_Object obj)
565 {
566 return (EVENTP (obj)) ? XEVENT(obj) : NULL;
567 }
568
569 struct Lisp_Cons *DCONS(Lisp_Object obj)
570 {
571 return (CONSP (obj)) ? XCONS(obj) : NULL;
572 }
573
574 Lisp_Object DCAR(Lisp_Object obj)
575 {
576 return (CONSP (obj)) ? XCAR(obj) : 0;
577 }
578
579 Lisp_Object DCDR(Lisp_Object obj)
580 {
581 return (CONSP (obj)) ? XCDR(obj) : 0;
582 }
583
584 char *DSTRING(Lisp_Object obj)
585 {
586 return (STRINGP (obj)) ? XSTRING_DATA(obj) : NULL;
587 }
588
589 struct Lisp_Vector *DVECTOR(Lisp_Object obj)
590 {
591 return (VECTORP (obj)) ? XVECTOR(obj) : NULL;
592 }
593
594 struct Lisp_Symbol *DSYMBOL(Lisp_Object obj)
595 {
596 return (SYMBOLP (obj)) ? XSYMBOL(obj) : NULL;
597 }
598
599 char *DSYMNAME(Lisp_Object obj)
600 {
601 return (SYMBOLP (obj)) ? XSYMBOL(obj)->name->_data : NULL;
602 }