]> code.delx.au - gnu-emacs/blob - src/w32xfns.c
; Merge from origin/emacs-25
[gnu-emacs] / src / w32xfns.c
1 /* Functions taken directly from X sources for use with the Microsoft Windows API.
2 Copyright (C) 1989, 1992-1995, 1999, 2001-2016 Free Software
3 Foundation, Inc.
4
5 This file is part of GNU Emacs.
6
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or (at
10 your option) any later version.
11
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
19
20 #include <config.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <windows.h>
24 #include <windowsx.h>
25
26 #include "lisp.h"
27 #include "frame.h"
28 #include "w32term.h"
29
30 #define myalloc(cb) GlobalAllocPtr (GPTR, cb)
31 #define myfree(lp) GlobalFreePtr (lp)
32
33 CRITICAL_SECTION critsect;
34
35 #ifdef WINDOWSNT
36 extern HANDLE keyboard_handle;
37 #endif /* WINDOWSNT */
38
39 HANDLE input_available = NULL;
40 HANDLE interrupt_handle = NULL;
41
42 void
43 init_crit (void)
44 {
45 InitializeCriticalSection (&critsect);
46
47 /* For safety, input_available should only be reset by get_next_msg
48 when the input queue is empty, so make it a manual reset event. */
49 input_available = CreateEvent (NULL, TRUE, FALSE, NULL);
50
51 /* Initialize the linked list of notifications sets that will be
52 used to communicate between the watching worker threads and the
53 main thread. */
54 notifications_set_head = malloc (sizeof(struct notifications_set));
55 if (notifications_set_head)
56 {
57 memset (notifications_set_head, 0, sizeof(struct notifications_set));
58 notifications_set_head->next
59 = notifications_set_head->prev = notifications_set_head;
60 }
61 else
62 DebPrint(("Out of memory: can't initialize notifications sets."));
63
64 #ifdef WINDOWSNT
65 keyboard_handle = input_available;
66 #endif /* WINDOWSNT */
67
68 /* interrupt_handle is signaled when quit (C-g) is detected, so that
69 blocking system calls can be interrupted. We make it a manual
70 reset event, so that if we should ever have multiple threads
71 performing system calls, they will all be interrupted (I'm guessing
72 that would the right response). Note that we use PulseEvent to
73 signal this event, so that it never remains signaled. */
74 interrupt_handle = CreateEvent (NULL, TRUE, FALSE, NULL);
75 }
76
77 void
78 delete_crit (void)
79 {
80 DeleteCriticalSection (&critsect);
81
82 if (input_available)
83 {
84 CloseHandle (input_available);
85 input_available = NULL;
86 }
87 if (interrupt_handle)
88 {
89 CloseHandle (interrupt_handle);
90 interrupt_handle = NULL;
91 }
92
93 if (notifications_set_head)
94 {
95 /* Free any remaining notifications set that could be left over. */
96 while (notifications_set_head->next != notifications_set_head)
97 {
98 struct notifications_set *ns = notifications_set_head->next;
99 notifications_set_head->next = ns->next;
100 ns->next->prev = notifications_set_head;
101 if (ns->notifications)
102 free (ns->notifications);
103 free (ns);
104 }
105 }
106 free (notifications_set_head);
107 }
108
109 void
110 signal_quit (void)
111 {
112 /* Make sure this event never remains signaled; if the main thread
113 isn't in a blocking call, then this should do nothing. */
114 PulseEvent (interrupt_handle);
115 }
116
117 void
118 select_palette (struct frame *f, HDC hdc)
119 {
120 struct w32_display_info *display_info = FRAME_DISPLAY_INFO (f);
121
122 if (!display_info->has_palette)
123 return;
124
125 if (display_info->palette == 0)
126 return;
127
128 if (!NILP (Vw32_enable_palette))
129 f->output_data.w32->old_palette =
130 SelectPalette (hdc, display_info->palette, FALSE);
131 else
132 f->output_data.w32->old_palette = NULL;
133
134 if (RealizePalette (hdc) != GDI_ERROR)
135 {
136 Lisp_Object frame, framelist;
137 FOR_EACH_FRAME (framelist, frame)
138 {
139 SET_FRAME_GARBAGED (XFRAME (frame));
140 }
141 }
142 }
143
144 void
145 deselect_palette (struct frame *f, HDC hdc)
146 {
147 if (f->output_data.w32->old_palette)
148 SelectPalette (hdc, f->output_data.w32->old_palette, FALSE);
149 }
150
151 /* Get a DC for frame and select palette for drawing; force an update of
152 all frames if palette's mapping changes. */
153 HDC
154 get_frame_dc (struct frame *f)
155 {
156 HDC hdc;
157
158 if (f->output_method != output_w32)
159 emacs_abort ();
160
161 enter_crit ();
162
163 hdc = GetDC (f->output_data.w32->window_desc);
164
165 /* If this gets called during startup before the frame is valid,
166 there is a chance of corrupting random data or crashing. */
167 if (hdc)
168 select_palette (f, hdc);
169
170 return hdc;
171 }
172
173 int
174 release_frame_dc (struct frame *f, HDC hdc)
175 {
176 int ret;
177
178 deselect_palette (f, hdc);
179 ret = ReleaseDC (f->output_data.w32->window_desc, hdc);
180
181 leave_crit ();
182
183 return ret;
184 }
185
186 typedef struct int_msg
187 {
188 W32Msg w32msg;
189 struct int_msg *lpNext;
190 } int_msg;
191
192 int_msg *lpHead = NULL;
193 int_msg *lpTail = NULL;
194 int nQueue = 0;
195
196 BOOL
197 get_next_msg (W32Msg * lpmsg, BOOL bWait)
198 {
199 BOOL bRet = FALSE;
200
201 enter_crit ();
202
203 /* The while loop takes care of multiple sets */
204
205 while (!nQueue && bWait)
206 {
207 leave_crit ();
208 WaitForSingleObject (input_available, INFINITE);
209 enter_crit ();
210 }
211
212 if (nQueue)
213 {
214 memcpy (lpmsg, &lpHead->w32msg, sizeof (W32Msg));
215
216 {
217 int_msg * lpCur = lpHead;
218
219 lpHead = lpHead->lpNext;
220
221 myfree (lpCur);
222 }
223
224 nQueue--;
225 /* Consolidate WM_PAINT messages to optimize redrawing. */
226 if (lpmsg->msg.message == WM_PAINT && nQueue)
227 {
228 int_msg * lpCur = lpHead;
229 int_msg * lpPrev = NULL;
230 int_msg * lpNext = NULL;
231
232 while (lpCur && nQueue)
233 {
234 lpNext = lpCur->lpNext;
235 if (lpCur->w32msg.msg.message == WM_PAINT)
236 {
237 /* Remove this message from the queue. */
238 if (lpPrev)
239 lpPrev->lpNext = lpNext;
240 else
241 lpHead = lpNext;
242
243 if (lpCur == lpTail)
244 lpTail = lpPrev;
245
246 /* Adjust clip rectangle to cover both. */
247 if (!UnionRect (&(lpmsg->rect), &(lpmsg->rect),
248 &(lpCur->w32msg.rect)))
249 {
250 SetRectEmpty (&(lpmsg->rect));
251 }
252
253 myfree (lpCur);
254
255 nQueue--;
256
257 lpCur = lpNext;
258 }
259 else
260 {
261 lpPrev = lpCur;
262 lpCur = lpNext;
263 }
264 }
265 }
266
267 bRet = TRUE;
268 }
269
270 if (nQueue == 0)
271 ResetEvent (input_available);
272
273 leave_crit ();
274
275 return (bRet);
276 }
277
278 extern char * w32_strerror (int error_no);
279
280 /* Tell the main thread that we have input available; if the main
281 thread is blocked in select(), we wake it up here. */
282 static void
283 notify_msg_ready (void)
284 {
285 SetEvent (input_available);
286
287 #ifdef CYGWIN
288 /* Wakes up the main thread, which is blocked select()ing for /dev/windows,
289 among other files. */
290 (void) PostThreadMessage (dwMainThreadId, WM_EMACS_INPUT_READY, 0, 0);
291 #endif /* CYGWIN */
292 }
293
294 BOOL
295 post_msg (W32Msg * lpmsg)
296 {
297 int_msg * lpNew = (int_msg *) myalloc (sizeof (int_msg));
298
299 if (!lpNew)
300 return (FALSE);
301
302 memcpy (&lpNew->w32msg, lpmsg, sizeof (W32Msg));
303 lpNew->lpNext = NULL;
304
305 enter_crit ();
306
307 if (nQueue++)
308 {
309 lpTail->lpNext = lpNew;
310 }
311 else
312 {
313 lpHead = lpNew;
314 }
315
316 lpTail = lpNew;
317 notify_msg_ready ();
318 leave_crit ();
319
320 return (TRUE);
321 }
322
323 BOOL
324 prepend_msg (W32Msg *lpmsg)
325 {
326 int_msg * lpNew = (int_msg *) myalloc (sizeof (int_msg));
327
328 if (!lpNew)
329 return (FALSE);
330
331 memcpy (&lpNew->w32msg, lpmsg, sizeof (W32Msg));
332
333 enter_crit ();
334
335 nQueue++;
336 lpNew->lpNext = lpHead;
337 lpHead = lpNew;
338 notify_msg_ready ();
339 leave_crit ();
340
341 return (TRUE);
342 }
343
344 /* Process all messages in the current thread's queue. Value is 1 if
345 one of these messages was WM_EMACS_FILENOTIFY, zero otherwise. */
346 int
347 drain_message_queue (void)
348 {
349 MSG msg;
350 int retval = 0;
351
352 while (PeekMessage (&msg, NULL, 0, 0, PM_REMOVE))
353 {
354 if (msg.message == WM_EMACS_FILENOTIFY)
355 retval = 1;
356 TranslateMessage (&msg);
357 DispatchMessage (&msg);
358 }
359 return retval;
360 }