213
|
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 *
|
219
|
31 * X on UNIX may be bad, but the win32 API really really really sucks.
|
|
32 *
|
213
|
33 * Windows user-input type events are stored in a per-thread message queue
|
|
34 * and retrieved using GetMessage(). It is not possible to wait on this
|
|
35 * queue and on other events (eg process input) simultaneously. Also, the
|
|
36 * main event-handling code in windows (the "windows procedure") is called
|
|
37 * asynchronously when windows has certain other types of events ("nonqueued
|
|
38 * messages") to deliver. The documentation doesn't appear to specify the
|
|
39 * context in which the windows procedure is called, but I assume that the
|
219
|
40 * thread that created the window is temporarily highjacked for this purpose
|
|
41 * when it calls GetMessage (a bit like X callbacks?).
|
213
|
42 *
|
219
|
43 * We spawn off a single thread to deal with both queued and non-queued
|
|
44 * events. The thread turns both kinds of events into emacs_events and stuffs
|
|
45 * them in a queue which XEmacs reads at its leisure. This file contains the
|
|
46 * code for that thread.
|
213
|
47 *
|
219
|
48 * Unfortunately, under win32 a seemingly-random selection of resources are
|
|
49 * owned by the thread that created/asked for them and not by the process. In
|
|
50 * particular, only the thread that created a window can retrieve messages
|
213
|
51 * destined for that window ("GetMessage does not retrieve messages for
|
|
52 * windows that belong to other threads..."). This means that our message-
|
219
|
53 * processing thread also has to do all window creation, deletion and various
|
|
54 * other random stuff. We handle this bogosity by getting the main XEmacs
|
|
55 * thread to send special user-defined messages to the message-processing
|
|
56 * thread to instruct it to create windows etc.
|
|
57 *
|
|
58 * More bogosity: Windows95 doesn't offer any one-shot timers, only a
|
|
59 * periodic timer. Worse, if you don't want a periodic timer to be associated
|
|
60 * with a particular mswindows window (we don't) your periodic timers don't
|
|
61 * have unique ids associated with them. We get round this lameness by
|
|
62 * setting off a single periodic timer and we use this to schedule timeouts
|
|
63 * manually. Implementing basic stuff like one-shot timers at the application
|
|
64 * level is not particularly efficient, but Windows95 leaves us no choice.
|
213
|
65 */
|
|
66
|
|
67
|
|
68 #include <config.h>
|
|
69 #include "lisp.h"
|
|
70
|
|
71 #include "console-msw.h"
|
|
72 #include "device.h"
|
|
73 #include "frame.h"
|
|
74 #include "events.h"
|
|
75 #include "event-msw.h"
|
|
76
|
219
|
77 #ifdef DEBUG_XEMACS
|
|
78 # include "opaque.h" /* For the debug functions at the end of this file */
|
|
79 # undef DEBUG_MESSAGES
|
|
80 # undef DEBUG_TIMEOUTS
|
|
81 #endif
|
|
82
|
213
|
83 #define MSWINDOWS_FRAME_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_TILEDWINDOW
|
|
84 #define MSWINDOWS_POPUP_STYLE WS_CLIPCHILDREN|WS_CLIPSIBLINGS|WS_CAPTION|WS_POPUP
|
|
85
|
219
|
86 static LRESULT WINAPI mswindows_wnd_proc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
213
|
87 static Lisp_Object mswindows_find_console (HWND hwnd);
|
|
88 static Lisp_Object mswindows_find_frame (HWND hwnd);
|
219
|
89 static Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods);
|
|
90 static int mswindows_modifier_state (void);
|
|
91 static int mswindows_enqueue_timeout (int milliseconds);
|
|
92 static void mswindows_dequeue_timeout (int interval_id);
|
|
93
|
|
94 /* Virtual keycode of the '@' key */
|
|
95 static int virtual_at_key;
|
|
96
|
|
97 /* Timeout queue */
|
|
98 struct mswindows_timeout
|
|
99 {
|
|
100 int ticks;
|
|
101 int interval_id;
|
|
102 struct mswindows_timeout *next;
|
|
103 };
|
|
104 typedef struct mswindows_timeout mswindows_timeout;
|
|
105 static mswindows_timeout timeout_pool[MSW_TIMEOUT_MAX];
|
|
106 static mswindows_timeout *timeout_head = NULL;
|
|
107 static int timeout_mswindows_id;
|
213
|
108
|
|
109 /*
|
|
110 * Entry point for the "windows" message-processing thread
|
|
111 */
|
|
112 DWORD mswindows_win_thread()
|
|
113 {
|
|
114 WNDCLASS wc;
|
|
115 MSG msg;
|
|
116 mswindows_waitable_info_type info;
|
|
117
|
|
118 /* Register the main window class */
|
|
119 wc.style = CS_OWNDC; /* One DC per window */
|
|
120 wc.lpfnWndProc = (WNDPROC) mswindows_wnd_proc;
|
|
121 wc.cbClsExtra = 0;
|
|
122 wc.cbWndExtra = 0; /* ? */
|
|
123 wc.hInstance = NULL; /* ? */
|
|
124 wc.hIcon = LoadIcon (NULL, XEMACS_CLASS);
|
|
125 wc.hCursor = LoadCursor (NULL, IDC_ARROW);
|
|
126 wc.hbrBackground = NULL; /* GetStockObject (WHITE_BRUSH); */
|
|
127 wc.lpszMenuName = NULL; /* XXX FIXME? Add a menu? */
|
|
128 wc.lpszClassName = XEMACS_CLASS;
|
|
129 RegisterClass(&wc); /* XXX FIXME: Should use RegisterClassEx */
|
|
130
|
|
131 info.type = mswindows_waitable_type_dispatch;
|
|
132 mswindows_add_waitable(&info);
|
|
133
|
|
134 /* Ensure our message queue is created XXX FIXME: Is this necessary? */
|
|
135 PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE);
|
|
136
|
|
137 /* Notify the main thread that we're ready */
|
|
138 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0));
|
|
139
|
219
|
140 /* Hack! Windows doesn't report Ctrl-@ characters so we have to find out
|
|
141 * which virtual key generates '@' at runtime */
|
|
142 virtual_at_key = VkKeyScan ('@');
|
|
143 if (virtual_at_key & 0x200) /* 0x200 means the control key */
|
|
144 /* If you need Ctrl just to generate @, you can't do Ctrl-@ */
|
|
145 virtual_at_key = -1;
|
|
146 else
|
|
147 virtual_at_key &= 0xff; /* The low byte contains the keycode */
|
|
148
|
213
|
149 /* Main windows loop */
|
|
150 while (1)
|
|
151 {
|
|
152 GetMessage (&msg, NULL, 0, 0);
|
|
153
|
|
154 /*
|
|
155 * Process things that don't have an associated window, so wouldn't
|
|
156 * get sent to mswindows_wnd_proc
|
|
157 */
|
|
158
|
|
159 /* Request from main thread */
|
|
160 if (msg.message>=WM_XEMACS_BASE && msg.message<=WM_XEMACS_END)
|
|
161 mswindows_handle_request(&msg);
|
|
162
|
219
|
163 /* Timeout(s) */
|
213
|
164 else if (msg.message == WM_TIMER)
|
|
165 {
|
219
|
166 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
167 if (timeout_head!=NULL)
|
|
168 --(timeout_head->ticks);
|
|
169
|
|
170 while (timeout_head!=NULL && timeout_head->ticks==0)
|
|
171 {
|
|
172 Lisp_Object emacs_event;
|
|
173 struct Lisp_Event *event;
|
|
174 int id = timeout_head->interval_id;
|
213
|
175
|
219
|
176 #ifdef DEBUG_TIMEOUTS
|
|
177 stderr_out("--> %x\n", id);
|
|
178 #endif
|
|
179 mswindows_dequeue_timeout (id);
|
|
180 emacs_event = Fmake_event (Qnil, Qnil);
|
|
181 event = XEVENT(emacs_event);
|
213
|
182
|
219
|
183 event->channel = Qnil;
|
|
184 event->timestamp = msg.time;
|
|
185 event->event_type = timeout_event;
|
|
186 event->event.timeout.interval_id = id;
|
|
187 mswindows_enqueue_dispatch_event (emacs_event);
|
|
188 }
|
213
|
189 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
190 }
|
|
191 else
|
|
192 /* Pass on to mswindows_wnd_proc */
|
|
193 DispatchMessage (&msg);
|
|
194 }
|
|
195 }
|
|
196
|
|
197 /*
|
|
198 * The windows procedure for the window class XEMACS_CLASS
|
|
199 * Stuffs messages in the mswindows event queue
|
|
200 */
|
|
201 static LRESULT WINAPI mswindows_wnd_proc(HWND hwnd, UINT message, WPARAM wParam,
|
|
202 LPARAM lParam)
|
|
203 {
|
|
204 /* Note: Remember to initialise these before use */
|
|
205 Lisp_Object emacs_event;
|
|
206 struct Lisp_Event *event;
|
|
207
|
221
|
208 static sizing = 0;
|
213
|
209 MSG msg = { hwnd, message, wParam, lParam, 0, {0,0} };
|
|
210 msg.time = GetMessageTime();
|
|
211
|
219
|
212 #ifdef DEBUG_MESSAGES
|
213
|
213 stderr_out("Message %04x, wParam=%04x, lParam=%08lx\n", message, wParam, lParam);
|
|
214 #endif
|
|
215 switch (message)
|
|
216 {
|
|
217 case WM_KEYDOWN:
|
|
218 case WM_SYSKEYDOWN:
|
|
219 {
|
|
220 /* Handle those keys that TranslateMessage won't generate a WM_CHAR for */
|
219
|
221 Lisp_Object keysym;
|
|
222 int mods = mswindows_modifier_state();
|
|
223
|
|
224 if (!NILP (keysym = mswindows_key_to_emacs_keysym(wParam, mods)))
|
213
|
225 {
|
|
226 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
227 emacs_event = Fmake_event (Qnil, Qnil);
|
|
228 event = XEVENT(emacs_event);
|
|
229
|
|
230 event->channel = mswindows_find_console(hwnd);
|
|
231 event->timestamp = msg.time;
|
|
232 event->event_type = key_press_event;
|
|
233 event->event.key.keysym = keysym;
|
|
234 event->event.key.modifiers = mods;
|
|
235 mswindows_enqueue_dispatch_event (emacs_event);
|
|
236 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
237 return (0);
|
|
238 }
|
|
239 }
|
|
240 TranslateMessage (&msg); /* Maybe generates WM_[SYS]CHAR in message queue */
|
|
241 goto defproc;
|
|
242
|
|
243 case WM_CHAR:
|
|
244 case WM_SYSCHAR:
|
|
245 {
|
|
246 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
247 emacs_event = Fmake_event (Qnil, Qnil);
|
|
248 event = XEVENT(emacs_event);
|
|
249
|
|
250 event->channel = mswindows_find_console(hwnd);
|
|
251 event->timestamp = msg.time;
|
|
252 event->event_type = key_press_event;
|
219
|
253
|
|
254 /* XEmacs doesn't seem to like Shift on non-alpha keys */
|
|
255 event->event.key.modifiers = isalpha(wParam) ?
|
|
256 mswindows_modifier_state() :
|
|
257 mswindows_modifier_state() & ~MOD_SHIFT;
|
|
258
|
|
259 if (wParam<' ') /* Control char not already handled under WM_KEYDOWN */
|
213
|
260 {
|
219
|
261 /* Don't capitalise alpha control keys */
|
|
262 event->event.key.keysym = isalpha(wParam+'a'-1) ?
|
|
263 make_char(wParam+'a'-1) :
|
|
264 make_char(wParam+'A'-1);
|
213
|
265 }
|
|
266 else
|
|
267 {
|
|
268 /* Assumes that emacs keysym == ASCII code */
|
|
269 event->event.key.keysym = make_char(wParam);
|
|
270 }
|
219
|
271
|
213
|
272 mswindows_enqueue_dispatch_event (emacs_event);
|
|
273 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
274 }
|
|
275 break;
|
|
276
|
|
277 case WM_LBUTTONDOWN:
|
|
278 case WM_MBUTTONDOWN:
|
|
279 case WM_RBUTTONDOWN:
|
|
280 case WM_LBUTTONUP:
|
|
281 case WM_MBUTTONUP:
|
|
282 case WM_RBUTTONUP:
|
|
283 {
|
|
284 /* XXX FIXME: Do middle button emulation */
|
219
|
285 short x, y;
|
|
286
|
213
|
287 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
288 emacs_event = Fmake_event (Qnil, Qnil);
|
|
289 event = XEVENT(emacs_event);
|
|
290
|
|
291 event->channel = mswindows_find_frame(hwnd);
|
|
292 event->timestamp = msg.time;
|
|
293 event->event.button.button =
|
|
294 (message==WM_LBUTTONDOWN || message==WM_LBUTTONUP) ? 1 :
|
|
295 ((message==WM_RBUTTONDOWN || message==WM_RBUTTONUP) ? 3 : 2);
|
219
|
296 x = LOWORD (lParam);
|
|
297 y = HIWORD (lParam);
|
|
298 event->event.button.x = x;
|
|
299 event->event.button.y = y;
|
|
300 event->event.button.modifiers = mswindows_modifier_state();
|
213
|
301
|
219
|
302 if (message==WM_LBUTTONDOWN || message==WM_MBUTTONDOWN ||
|
|
303 message==WM_RBUTTONDOWN)
|
|
304 {
|
|
305 event->event_type = button_press_event;
|
|
306 SetCapture (hwnd);
|
|
307 }
|
|
308 else
|
|
309 {
|
|
310 event->event_type = button_release_event;
|
|
311 ReleaseCapture ();
|
|
312 }
|
|
313
|
213
|
314 mswindows_enqueue_dispatch_event (emacs_event);
|
|
315 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
316 }
|
|
317 break;
|
|
318
|
|
319 case WM_MOUSEMOVE:
|
221
|
320 /* Optimization: don't report mouse movement while size is changind */
|
|
321 if (!sizing)
|
213
|
322 {
|
219
|
323 short x, y;
|
|
324
|
213
|
325 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
326 emacs_event = Fmake_event (Qnil, Qnil);
|
|
327 event = XEVENT(emacs_event);
|
|
328
|
|
329 event->channel = mswindows_find_frame(hwnd);
|
|
330 event->timestamp = msg.time;
|
|
331 event->event_type = pointer_motion_event;
|
219
|
332 x = LOWORD (lParam);
|
|
333 y = HIWORD (lParam);
|
|
334 event->event.motion.x = x;
|
|
335 event->event.motion.y = y;
|
|
336 event->event.motion.modifiers = mswindows_modifier_state();
|
213
|
337
|
|
338 mswindows_enqueue_dispatch_event (emacs_event);
|
|
339 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
340 }
|
|
341 break;
|
|
342
|
|
343 case WM_PAINT:
|
|
344 if (GetUpdateRect(hwnd, NULL, FALSE))
|
|
345 {
|
|
346 PAINTSTRUCT paintStruct;
|
|
347
|
|
348 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
349 emacs_event = Fmake_event (Qnil, Qnil);
|
|
350 event = XEVENT(emacs_event);
|
|
351
|
|
352 event->channel = mswindows_find_frame(hwnd);
|
|
353 event->timestamp = msg.time;
|
|
354 event->event_type = magic_event;
|
|
355 BeginPaint (hwnd, &paintStruct);
|
|
356 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message;
|
|
357 EVENT_MSWINDOWS_MAGIC_DATA(event) = paintStruct.rcPaint;
|
|
358 EndPaint (hwnd, &paintStruct);
|
|
359
|
|
360 mswindows_enqueue_dispatch_event (emacs_event);
|
|
361 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
362 }
|
|
363 break;
|
|
364
|
|
365 case WM_SIZE:
|
|
366 /* We only care about this message if our size has really changed */
|
|
367 if (wParam==SIZE_RESTORED || wParam==SIZE_MAXIMIZED || wParam==SIZE_MINIMIZED)
|
|
368 {
|
|
369 RECT rect;
|
|
370 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
371 emacs_event = Fmake_event (Qnil, Qnil);
|
|
372 event = XEVENT(emacs_event);
|
|
373
|
|
374 event->channel = mswindows_find_frame(hwnd);
|
|
375 event->timestamp = msg.time;
|
|
376 event->event_type = magic_event;
|
|
377 if (wParam==SIZE_MINIMIZED)
|
|
378 rect.left = rect.top = rect.right = rect.bottom = -1;
|
|
379 else
|
|
380 GetClientRect(hwnd, &rect);
|
|
381 EVENT_MSWINDOWS_MAGIC_TYPE(event) = message;
|
|
382 EVENT_MSWINDOWS_MAGIC_DATA(event) = rect;
|
|
383
|
|
384 mswindows_enqueue_dispatch_event (emacs_event);
|
|
385 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
386 }
|
|
387 break;
|
|
388
|
219
|
389 /* Misc magic events which only require that the frame be identified */
|
213
|
390 case WM_SETFOCUS:
|
|
391 case WM_KILLFOCUS:
|
219
|
392 case WM_CLOSE:
|
213
|
393 {
|
|
394 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
395 emacs_event = Fmake_event (Qnil, Qnil);
|
219
|
396 event = XEVENT (emacs_event);
|
213
|
397
|
219
|
398 event->channel = mswindows_find_frame (hwnd);
|
213
|
399 event->timestamp = msg.time;
|
|
400 event->event_type = magic_event;
|
219
|
401 EVENT_MSWINDOWS_MAGIC_TYPE (event) = message;
|
213
|
402
|
|
403 mswindows_enqueue_dispatch_event (emacs_event);
|
|
404 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
405 }
|
|
406 break;
|
|
407
|
219
|
408 case WM_WINDOWPOSCHANGING:
|
|
409 {
|
|
410 WINDOWPOS *wp = (LPWINDOWPOS) lParam;
|
|
411 WINDOWPLACEMENT wpl = { sizeof(WINDOWPLACEMENT) };
|
|
412 GetWindowPlacement(hwnd, &wpl);
|
|
413
|
|
414 /* Only interested if size is changing and we're not being iconified */
|
|
415 if ((wpl.showCmd != SW_SHOWMINIMIZED) && !(wp->flags & SWP_NOSIZE))
|
|
416 {
|
|
417 RECT ncsize = { 0, 0, 0, 0 };
|
|
418 int pixwidth, pixheight;
|
|
419 AdjustWindowRect (&ncsize, GetWindowLong (hwnd, GWL_STYLE), FALSE);
|
|
420
|
|
421 round_size_to_char (XFRAME (mswindows_find_frame (hwnd)),
|
|
422 wp->cx - (ncsize.right - ncsize.left),
|
|
423 wp->cy - (ncsize.bottom - ncsize.top),
|
|
424 &pixwidth, &pixheight);
|
|
425
|
|
426 /* Convert client sizes to window sizes */
|
|
427 pixwidth += (ncsize.right - ncsize.left);
|
|
428 pixheight += (ncsize.bottom - ncsize.top);
|
|
429
|
|
430 if (wpl.showCmd != SW_SHOWMAXIMIZED)
|
|
431 {
|
|
432 /* Adjust so that the bottom or right doesn't move if it's
|
|
433 * the top or left that's being changed */
|
|
434 RECT rect;
|
|
435 GetWindowRect (hwnd, &rect);
|
|
436
|
|
437 if (rect.left != wp->x)
|
|
438 wp->x += wp->cx - pixwidth;
|
|
439 if (rect.top != wp->y)
|
|
440 wp->y += wp->cy - pixheight;
|
|
441 }
|
|
442
|
|
443 wp->cx = pixwidth;
|
|
444 wp->cy = pixheight;
|
|
445 }
|
|
446 }
|
|
447 break;
|
|
448
|
221
|
449 case WM_ENTERSIZEMOVE:
|
|
450 case WM_EXITSIZEMOVE:
|
|
451 sizing = (message == WM_ENTERSIZEMOVE);
|
|
452 goto defproc;
|
|
453
|
213
|
454 defproc:
|
|
455 default:
|
|
456 return DefWindowProc (hwnd, message, wParam, lParam);
|
|
457 }
|
|
458 return (0);
|
|
459 }
|
|
460
|
|
461
|
|
462 /*
|
|
463 * Make a request to the message-processing thread to do things that
|
|
464 * can't be done in the main thread.
|
|
465 */
|
|
466 LPARAM
|
|
467 mswindows_make_request(UINT message, WPARAM wParam, mswindows_request_type *request)
|
|
468 {
|
|
469 MSG msg;
|
|
470 assert(PostThreadMessage (mswindows_win_thread_id, message, wParam,
|
|
471 (LPARAM) request));
|
|
472 GetMessage (&msg, NULL, WM_XEMACS_ACK, WM_XEMACS_ACK);
|
|
473 return (msg.lParam);
|
|
474 }
|
|
475
|
|
476
|
|
477 /*
|
|
478 * Handle a request from the main thread to do things that have to be
|
|
479 * done in the message-processing thread.
|
|
480 */
|
|
481 static void
|
|
482 mswindows_handle_request (MSG *msg)
|
|
483 {
|
|
484 mswindows_request_type *request = (mswindows_request_type *) msg->lParam;
|
|
485
|
|
486 switch (msg->message)
|
|
487 {
|
|
488 case WM_XEMACS_CREATEWINDOW:
|
|
489 {
|
|
490 struct frame *f = request->thing1;
|
|
491 Lisp_Object *props = request->thing2;
|
|
492 Lisp_Object name, height, width, popup, top, left;
|
217
|
493 int pixel_width, pixel_height;
|
213
|
494 RECT rect;
|
|
495 DWORD style;
|
|
496 HWND hwnd;
|
|
497
|
|
498 name = Fplist_get (*props, Qname, Qnil);
|
|
499 height = Fplist_get (*props, Qheight, Qnil);
|
|
500 width = Fplist_get (*props, Qwidth, Qnil);
|
|
501 popup = Fplist_get (*props, Qpopup, Qnil);
|
|
502 top = Fplist_get (*props, Qtop, Qnil);
|
|
503 left = Fplist_get (*props, Qleft, Qnil);
|
|
504
|
|
505 style = (NILP(popup)) ? MSWINDOWS_FRAME_STYLE : MSWINDOWS_POPUP_STYLE;
|
|
506
|
221
|
507 FRAME_WIDTH (f) = INTP(width) ? XINT(width) : 80;
|
|
508 FRAME_HEIGHT (f) = INTP(height) ? XINT(height) : 30;
|
|
509 char_to_pixel_size (f, FRAME_WIDTH(f), FRAME_HEIGHT (f),
|
|
510 &FRAME_PIXWIDTH (f), &FRAME_PIXHEIGHT (f));
|
|
511
|
|
512 rect.left = rect.top = 0;
|
|
513 rect.right = FRAME_PIXWIDTH (f);
|
|
514 rect.bottom = FRAME_PIXHEIGHT (f);
|
213
|
515 #ifdef HAVE_MENUBARS
|
|
516 AdjustWindowRect(&rect, style, TRUE);
|
|
517 #else
|
|
518 AdjustWindowRect(&rect, style, FALSE);
|
|
519 #endif
|
|
520
|
|
521 hwnd = CreateWindow (XEMACS_CLASS,
|
|
522 STRINGP(f->name) ? XSTRING_DATA(f->name) :
|
|
523 (STRINGP(name) ? XSTRING_DATA(name) : XEMACS_CLASS),
|
|
524 style,
|
|
525 INTP(left) ? XINT(left) : CW_USEDEFAULT,
|
|
526 INTP(top) ? XINT(top) : CW_USEDEFAULT,
|
|
527 rect.right-rect.left, rect.bottom-rect.top,
|
|
528 NULL, NULL, NULL, NULL);
|
|
529 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, (LPARAM) hwnd));
|
|
530 }
|
|
531 return;
|
|
532
|
219
|
533 case WM_XEMACS_DESTROYWINDOW:
|
|
534 {
|
|
535 struct frame *f = request->thing1;
|
|
536 ReleaseDC(FRAME_MSWINDOWS_HANDLE(f), FRAME_MSWINDOWS_DC(f));
|
|
537 DestroyWindow(FRAME_MSWINDOWS_HANDLE(f));
|
|
538 assert (PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0));
|
|
539 }
|
|
540 break;
|
|
541
|
213
|
542 case WM_XEMACS_SETTIMER:
|
|
543 {
|
219
|
544 int id;
|
|
545 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
546 id = mswindows_enqueue_timeout((int) request->thing1);
|
|
547 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
548 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, id));
|
213
|
549 }
|
|
550 break;
|
|
551
|
|
552 case WM_XEMACS_KILLTIMER:
|
|
553 {
|
219
|
554 EnterCriticalSection (&mswindows_dispatch_crit);
|
|
555 mswindows_dequeue_timeout((int) request->thing1);
|
|
556 LeaveCriticalSection (&mswindows_dispatch_crit);
|
|
557 assert(PostThreadMessage (mswindows_main_thread_id, WM_XEMACS_ACK, 0, 0));
|
213
|
558 }
|
|
559 break;
|
|
560
|
|
561 default:
|
|
562 assert(0);
|
|
563 }
|
|
564 }
|
|
565
|
|
566
|
219
|
567 /* Returns the state of the modifier keys in the format expected by the
|
|
568 * Lisp_Event key_data, button_data and motion_data modifiers member */
|
|
569 int mswindows_modifier_state (void)
|
|
570 {
|
|
571 /* Set high bit of GetKeyState's return value indicates the key is down */
|
|
572 return ((GetKeyState (VK_SHIFT) & 0x8000) ? MOD_SHIFT : 0) |
|
|
573 ((GetKeyState (VK_CONTROL) & 0x8000) ? MOD_CONTROL: 0) |
|
|
574 ((GetKeyState (VK_MENU) & 0x8000) ? MOD_META : 0);
|
|
575 }
|
|
576
|
|
577
|
213
|
578 /*
|
|
579 * Translate a mswindows virtual key to a keysym.
|
|
580 * Only returns non-Qnil for keys that don't generate WM_CHAR messages
|
|
581 * or whose ASCII codes (like space) xemacs doesn't like.
|
|
582 * Virtual key values are defined in winresrc.h
|
|
583 * XXX I'm not sure that KEYSYM("name") is the best thing to use here.
|
|
584 */
|
219
|
585 Lisp_Object mswindows_key_to_emacs_keysym(int mswindows_key, int mods)
|
213
|
586 {
|
|
587 switch (mswindows_key)
|
|
588 {
|
|
589 /* First the predefined ones */
|
|
590 case VK_BACK: return QKbackspace;
|
|
591 case VK_TAB: return QKtab;
|
|
592 case '\n': return QKlinefeed; /* No VK_LINEFEED in winresrc.h */
|
|
593 case VK_RETURN: return QKreturn;
|
|
594 case VK_ESCAPE: return QKescape;
|
|
595 case VK_SPACE: return QKspace;
|
|
596 case VK_DELETE: return QKdelete;
|
|
597
|
|
598 /* The rest */
|
219
|
599 case VK_CLEAR: return KEYSYM ("clear"); /* Should do ^L ? */
|
213
|
600 case VK_PRIOR: return KEYSYM ("prior");
|
|
601 case VK_NEXT: return KEYSYM ("next");
|
|
602 case VK_END: return KEYSYM ("end");
|
|
603 case VK_HOME: return KEYSYM ("home");
|
|
604 case VK_LEFT: return KEYSYM ("left");
|
|
605 case VK_UP: return KEYSYM ("up");
|
|
606 case VK_RIGHT: return KEYSYM ("right");
|
|
607 case VK_DOWN: return KEYSYM ("down");
|
219
|
608 case VK_SELECT: return KEYSYM ("select");
|
|
609 case VK_PRINT: return KEYSYM ("print");
|
|
610 case VK_EXECUTE: return KEYSYM ("execute");
|
|
611 case VK_SNAPSHOT: return KEYSYM ("print");
|
213
|
612 case VK_INSERT: return KEYSYM ("insert");
|
|
613 case VK_HELP: return KEYSYM ("help");
|
219
|
614 #if 0 /* XXX What are these supposed to do? */
|
|
615 case VK_LWIN return KEYSYM ("");
|
|
616 case VK_RWIN return KEYSYM ("");
|
|
617 #endif
|
|
618 case VK_APPS: return KEYSYM ("menu");
|
221
|
619 case VK_F1: return KEYSYM ("f1");
|
|
620 case VK_F2: return KEYSYM ("f2");
|
|
621 case VK_F3: return KEYSYM ("f3");
|
|
622 case VK_F4: return KEYSYM ("f4");
|
|
623 case VK_F5: return KEYSYM ("f5");
|
|
624 case VK_F6: return KEYSYM ("f6");
|
|
625 case VK_F7: return KEYSYM ("f7");
|
|
626 case VK_F8: return KEYSYM ("f8");
|
|
627 case VK_F9: return KEYSYM ("f9");
|
|
628 case VK_F10: return KEYSYM ("f10");
|
|
629 case VK_F11: return KEYSYM ("f11");
|
|
630 case VK_F12: return KEYSYM ("f12");
|
|
631 case VK_F13: return KEYSYM ("f13");
|
|
632 case VK_F14: return KEYSYM ("f14");
|
|
633 case VK_F15: return KEYSYM ("f15");
|
|
634 case VK_F16: return KEYSYM ("f16");
|
|
635 case VK_F17: return KEYSYM ("f17");
|
|
636 case VK_F18: return KEYSYM ("f18");
|
|
637 case VK_F19: return KEYSYM ("f19");
|
|
638 case VK_F20: return KEYSYM ("f20");
|
|
639 case VK_F21: return KEYSYM ("f21");
|
|
640 case VK_F22: return KEYSYM ("f22");
|
|
641 case VK_F23: return KEYSYM ("f23");
|
|
642 case VK_F24: return KEYSYM ("f24");
|
219
|
643 default:
|
|
644 /* Special handling for Ctrl-'@' because '@' lives shifted on varying
|
|
645 * virtual keys and because Windows doesn't report Ctrl-@ as a WM_CHAR */
|
|
646 if (((mods & (MOD_SHIFT|MOD_CONTROL)) == (MOD_SHIFT|MOD_CONTROL)) &&
|
|
647 (mswindows_key == virtual_at_key))
|
|
648 return make_char('@');
|
213
|
649 }
|
|
650 return Qnil;
|
|
651 }
|
|
652
|
|
653
|
|
654 /*
|
219
|
655 * Add a timeout to the queue. Returns the id or 0 on failure
|
|
656 */
|
|
657 static int mswindows_enqueue_timeout (int milliseconds)
|
|
658 {
|
|
659 static int timeout_last_interval_id;
|
|
660 int target_ticks = (milliseconds + MSW_TIMEOUT_GRANULARITY-1) /
|
|
661 MSW_TIMEOUT_GRANULARITY;
|
|
662 mswindows_timeout *target;
|
|
663 int i;
|
|
664
|
|
665 /* Find a free timeout */
|
|
666 for (i=0; i<MSW_TIMEOUT_MAX; i++)
|
|
667 {
|
|
668 target = timeout_pool + i;
|
|
669 if (target->interval_id == 0)
|
|
670 break;
|
|
671 }
|
|
672
|
|
673 /* No free timeout */
|
|
674 if (i==MSW_TIMEOUT_MAX)
|
|
675 return 0;
|
|
676
|
|
677 if (++timeout_last_interval_id == 0)
|
|
678 ++timeout_last_interval_id;
|
|
679
|
|
680 if (timeout_head == NULL || timeout_head->ticks >= target_ticks)
|
|
681 {
|
|
682 /* First or only timeout in the queue (common case) */
|
|
683 target->interval_id = timeout_last_interval_id;
|
|
684 target->ticks = target_ticks;
|
|
685 target->next = timeout_head;
|
|
686 timeout_head = target;
|
|
687
|
|
688 if (target->next == NULL)
|
|
689 {
|
|
690 /* Queue was empty - restart the timer */
|
|
691 timeout_mswindows_id = SetTimer (NULL, 0, MSW_TIMEOUT_GRANULARITY,
|
|
692 NULL);
|
|
693 #ifdef DEBUG_TIMEOUTS
|
|
694 stderr_out("Start\n");
|
|
695 #endif
|
|
696 }
|
|
697 else
|
|
698 target->next->ticks -= target->ticks;
|
|
699 }
|
|
700 else
|
|
701 {
|
|
702 /* Find the timeout before this new one */
|
|
703 mswindows_timeout *prev = timeout_head;
|
|
704 int tick_count = prev->ticks; /* Number of ticks up to prev */
|
|
705
|
|
706 while (prev->next != NULL)
|
|
707 {
|
|
708 if (tick_count + prev->next->ticks >= target_ticks)
|
|
709 break;
|
|
710 prev = prev->next;
|
|
711 tick_count += prev->ticks;
|
|
712 }
|
|
713
|
|
714 /* Insert the new timeout in the queue */
|
|
715 target->interval_id = timeout_last_interval_id;
|
|
716 target->ticks = target_ticks - tick_count;
|
|
717 target->next = prev->next;
|
|
718 prev->next = target;
|
|
719 if (target->next != NULL)
|
|
720 target->next->ticks -= target->ticks;
|
|
721 }
|
|
722 #ifdef DEBUG_TIMEOUTS
|
|
723 stderr_out("Set %x %d %d\n", timeout_last_interval_id, target_ticks, milliseconds);
|
|
724 #endif
|
|
725 return timeout_last_interval_id;
|
|
726 }
|
|
727
|
|
728
|
|
729 /*
|
|
730 * Remove a timeout from the queue
|
|
731 */
|
|
732 static void mswindows_dequeue_timeout (int interval_id)
|
|
733 {
|
|
734 mswindows_timeout *target;
|
|
735 mswindows_timeout *prev;
|
|
736
|
|
737 target = timeout_head;
|
|
738 prev = NULL;
|
|
739 while (target != NULL)
|
|
740 {
|
|
741 if (target->interval_id == interval_id)
|
|
742 {
|
|
743 #ifdef DEBUG_TIMEOUTS
|
|
744 stderr_out("Kil %x %d\n", interval_id, target->ticks);
|
|
745 #endif
|
|
746 target->interval_id = 0; /* Mark free */
|
|
747
|
|
748 if (prev!=NULL)
|
|
749 {
|
|
750 prev->next = target->next;
|
|
751 if (target->next != NULL)
|
|
752 target->next->ticks += target->ticks;
|
|
753 }
|
|
754 else if ((timeout_head = target->next) == NULL)
|
|
755 {
|
|
756 /* Queue is now empty - stop the timer */
|
|
757 KillTimer (NULL, timeout_mswindows_id);
|
|
758 timeout_mswindows_id = 0;
|
|
759 #ifdef DEBUG_TIMEOUTS
|
|
760 stderr_out("Stop\n");
|
|
761 #endif
|
|
762 }
|
|
763 return;
|
|
764 }
|
|
765 else
|
|
766 {
|
|
767 prev = target;
|
|
768 target = target->next;
|
|
769 }
|
|
770 }
|
|
771
|
|
772 /* Ack! the timeout wasn't in the timeout queue which means that it's
|
|
773 * probably gone off and is now sitting in the dispatch queue. XEmacs will
|
|
774 * be very unhappy if it sees the timeout so we have to fish it out of the
|
|
775 * dispatch queue. This only happens if XEmacs can't keep up with events */
|
|
776 #ifdef DEBUG_TIMEOUTS
|
|
777 stderr_out("Kil %x - not found\n", interval_id);
|
|
778 #endif
|
|
779 {
|
|
780 Lisp_Object match_event, emacs_event;
|
|
781 struct Lisp_Event *event;
|
|
782 match_event = Fmake_event (Qnil, Qnil);
|
|
783 event = XEVENT(match_event);
|
|
784
|
|
785 event->channel = Qnil;
|
|
786 event->event_type = timeout_event;
|
|
787 event->event.timeout.interval_id = interval_id;
|
|
788 emacs_event = mswindows_cancel_dispatch_event (match_event);
|
|
789 if (!NILP (emacs_event))
|
|
790 Fdeallocate_event(emacs_event);
|
|
791 Fdeallocate_event(match_event);
|
|
792 }
|
|
793 }
|
|
794
|
|
795
|
|
796 /*
|
213
|
797 * Find the console that matches the supplied mswindows window handle
|
|
798 */
|
|
799 static Lisp_Object
|
|
800 mswindows_find_console (HWND hwnd)
|
|
801 {
|
|
802 Lisp_Object concons;
|
|
803
|
|
804 CONSOLE_LOOP (concons)
|
|
805 {
|
|
806 Lisp_Object console = XCAR (concons);
|
|
807 /* We only support one console so this must be it */
|
|
808 return console;
|
|
809 }
|
|
810
|
|
811 return Qnil;
|
|
812 }
|
|
813
|
|
814 /*
|
|
815 * Find the frame that matches the supplied mswindows window handle
|
|
816 */
|
|
817 static Lisp_Object
|
|
818 mswindows_find_frame (HWND hwnd)
|
|
819 {
|
|
820 Lisp_Object frmcons, devcons, concons;
|
|
821
|
|
822 FRAME_LOOP_NO_BREAK (frmcons, devcons, concons)
|
|
823 {
|
|
824 struct frame *f;
|
|
825 Lisp_Object frame = XCAR (frmcons);
|
|
826 f = XFRAME (frame);
|
|
827 if (FRAME_TYPE_P(f, mswindows)) /* Might be a stream-type frame */
|
|
828 if (FRAME_MSWINDOWS_HANDLE(f)==hwnd)
|
|
829 return frame;
|
|
830 }
|
|
831 assert(0); /* XXX Can't happen! we only get messages for our windows */
|
|
832 return Qnil;
|
|
833 }
|
|
834
|
219
|
835
|
|
836 #ifdef DEBUG_XEMACS
|
213
|
837 /*
|
|
838 * Random helper functions for debugging.
|
|
839 * Intended for use in the MSVC "Watch" window which doesn't like
|
|
840 * the aborts that the error_check_foo() functions can make.
|
|
841 */
|
|
842 struct lrecord_header *DHEADER(Lisp_Object obj)
|
|
843 {
|
219
|
844 return (LRECORDP (obj)) ? XRECORD_LHEADER (obj) : NULL;
|
|
845 }
|
|
846
|
|
847 int DOPAQUE_DATA (Lisp_Object obj)
|
|
848 {
|
|
849 return (OPAQUEP (obj)) ? OPAQUE_DATA (XOPAQUE (obj)) : NULL;
|
213
|
850 }
|
|
851
|
|
852 struct Lisp_Event *DEVENT(Lisp_Object obj)
|
|
853 {
|
219
|
854 return (EVENTP (obj)) ? XEVENT (obj) : NULL;
|
213
|
855 }
|
|
856
|
|
857 struct Lisp_Cons *DCONS(Lisp_Object obj)
|
|
858 {
|
219
|
859 return (CONSP (obj)) ? XCONS (obj) : NULL;
|
213
|
860 }
|
|
861
|
|
862 Lisp_Object DCAR(Lisp_Object obj)
|
|
863 {
|
219
|
864 return (CONSP (obj)) ? XCAR (obj) : 0;
|
213
|
865 }
|
|
866
|
|
867 Lisp_Object DCDR(Lisp_Object obj)
|
|
868 {
|
219
|
869 return (CONSP (obj)) ? XCDR (obj) : 0;
|
|
870 }
|
|
871
|
|
872 Lisp_Object DCONSCDR(Lisp_Object obj)
|
|
873 {
|
|
874 return ((CONSP (obj)) && (CONSP (XCDR (obj)))) ? XCONS (XCDR (obj)) : 0;
|
|
875 }
|
|
876
|
|
877 Lisp_Object DCARCDR(Lisp_Object obj)
|
|
878 {
|
|
879 return ((CONSP (obj)) && (CONSP (XCDR (obj)))) ? XCAR (XCDR (obj)) : 0;
|
213
|
880 }
|
|
881
|
|
882 char *DSTRING(Lisp_Object obj)
|
|
883 {
|
219
|
884 return (STRINGP (obj)) ? XSTRING_DATA (obj) : NULL;
|
213
|
885 }
|
|
886
|
|
887 struct Lisp_Vector *DVECTOR(Lisp_Object obj)
|
|
888 {
|
219
|
889 return (VECTORP (obj)) ? XVECTOR (obj) : NULL;
|
213
|
890 }
|
|
891
|
|
892 struct Lisp_Symbol *DSYMBOL(Lisp_Object obj)
|
|
893 {
|
219
|
894 return (SYMBOLP (obj)) ? XSYMBOL (obj) : NULL;
|
213
|
895 }
|
|
896
|
|
897 char *DSYMNAME(Lisp_Object obj)
|
|
898 {
|
219
|
899 return (SYMBOLP (obj)) ? XSYMBOL (obj)->name->_data : NULL;
|
213
|
900 }
|
219
|
901
|
|
902 #endif
|