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