mingw-hdep.c revision 1.9
1/* Host support routines for MinGW, for GDB, the GNU debugger.
2
3   Copyright (C) 2006-2020 Free Software Foundation, Inc.
4
5   This file is part of GDB.
6
7   This program 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
10   (at your option) any later version.
11
12   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
19
20#include "defs.h"
21#include "main.h"
22#include "serial.h"
23#include "gdbsupport/event-loop.h"
24
25#include "gdbsupport/gdb_select.h"
26
27#include <windows.h>
28
29/* Return an absolute file name of the running GDB, if possible, or
30   ARGV0 if not.  The return value is in malloc'ed storage.  */
31
32char *
33windows_get_absolute_argv0 (const char *argv0)
34{
35  char full_name[PATH_MAX];
36
37  if (GetModuleFileName (NULL, full_name, PATH_MAX))
38    return xstrdup (full_name);
39  return xstrdup (argv0);
40}
41
42/* Wrapper for select.  On Windows systems, where the select interface
43   only works for sockets, this uses the GDB serial abstraction to
44   handle sockets, consoles, pipes, and serial ports.
45
46   The arguments to this function are the same as the traditional
47   arguments to select on POSIX platforms.  */
48
49int
50gdb_select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
51	    struct timeval *timeout)
52{
53  static HANDLE never_handle;
54  HANDLE handles[MAXIMUM_WAIT_OBJECTS];
55  HANDLE h;
56  DWORD event;
57  DWORD num_handles;
58  /* SCBS contains serial control objects corresponding to file
59     descriptors in READFDS and WRITEFDS.  */
60  struct serial *scbs[MAXIMUM_WAIT_OBJECTS];
61  /* The number of valid entries in SCBS.  */
62  size_t num_scbs;
63  int fd;
64  int num_ready;
65  size_t indx;
66
67  if (n == 0)
68    {
69      /* The MS API says that the first argument to
70	 WaitForMultipleObjects cannot be zero.  That's why we just
71	 use a regular Sleep here.  */
72      if (timeout != NULL)
73	Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
74
75      return 0;
76    }
77
78  num_ready = 0;
79  num_handles = 0;
80  num_scbs = 0;
81  for (fd = 0; fd < n; ++fd)
82    {
83      HANDLE read = NULL, except = NULL;
84      struct serial *scb;
85
86      /* There is no support yet for WRITEFDS.  At present, this isn't
87	 used by GDB -- but we do not want to silently ignore WRITEFDS
88	 if something starts using it.  */
89      gdb_assert (!writefds || !FD_ISSET (fd, writefds));
90
91      if ((!readfds || !FD_ISSET (fd, readfds))
92	  && (!exceptfds || !FD_ISSET (fd, exceptfds)))
93	continue;
94
95      scb = serial_for_fd (fd);
96      if (scb)
97	{
98	  serial_wait_handle (scb, &read, &except);
99	  scbs[num_scbs++] = scb;
100	}
101
102      if (read == NULL)
103	read = (HANDLE) _get_osfhandle (fd);
104      if (except == NULL)
105	{
106	  if (!never_handle)
107	    never_handle = CreateEvent (0, FALSE, FALSE, 0);
108
109	  except = never_handle;
110	}
111
112      if (readfds && FD_ISSET (fd, readfds))
113	{
114	  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
115	  handles[num_handles++] = read;
116	}
117
118      if (exceptfds && FD_ISSET (fd, exceptfds))
119	{
120	  gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS);
121	  handles[num_handles++] = except;
122	}
123    }
124
125  gdb_assert (num_handles <= MAXIMUM_WAIT_OBJECTS);
126
127  event = WaitForMultipleObjects (num_handles,
128				  handles,
129				  FALSE,
130				  timeout
131				  ? (timeout->tv_sec * 1000
132				     + timeout->tv_usec / 1000)
133				  : INFINITE);
134  /* EVENT can only be a value in the WAIT_ABANDONED_0 range if the
135     HANDLES included an abandoned mutex.  Since GDB doesn't use
136     mutexes, that should never occur.  */
137  gdb_assert (!(WAIT_ABANDONED_0 <= event
138		&& event < WAIT_ABANDONED_0 + num_handles));
139  /* We no longer need the helper threads to check for activity.  */
140  for (indx = 0; indx < num_scbs; ++indx)
141    serial_done_wait_handle (scbs[indx]);
142  if (event == WAIT_FAILED)
143    return -1;
144  if (event == WAIT_TIMEOUT)
145    return 0;
146  /* Run through the READFDS, clearing bits corresponding to descriptors
147     for which input is unavailable.  */
148  h = handles[event - WAIT_OBJECT_0];
149  for (fd = 0, indx = 0; fd < n; ++fd)
150    {
151      HANDLE fd_h;
152
153      if ((!readfds || !FD_ISSET (fd, readfds))
154	  && (!exceptfds || !FD_ISSET (fd, exceptfds)))
155	continue;
156
157      if (readfds && FD_ISSET (fd, readfds))
158	{
159	  fd_h = handles[indx++];
160	  /* This handle might be ready, even though it wasn't the handle
161	     returned by WaitForMultipleObjects.  */
162	  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
163	    FD_CLR (fd, readfds);
164	  else
165	    num_ready++;
166	}
167
168      if (exceptfds && FD_ISSET (fd, exceptfds))
169	{
170	  fd_h = handles[indx++];
171	  /* This handle might be ready, even though it wasn't the handle
172	     returned by WaitForMultipleObjects.  */
173	  if (fd_h != h && WaitForSingleObject (fd_h, 0) != WAIT_OBJECT_0)
174	    FD_CLR (fd, exceptfds);
175	  else
176	    num_ready++;
177	}
178    }
179
180  return num_ready;
181}
182
183/* Map COLOR's RGB triplet, with 8 bits per component, into 16 Windows
184   console colors, where each component has just 1 bit, plus a single
185   intensity bit which affects all 3 components.  */
186static int
187rgb_to_16colors (const ui_file_style::color &color)
188{
189  uint8_t rgb[3];
190  color.get_rgb (rgb);
191
192  int retval = 0;
193  for (int i = 0; i < 3; i++)
194    {
195      /* Subdivide 256 possible values of each RGB component into 3
196	 regions: no color, normal color, bright color.  256 / 3 = 85,
197	 but ui-style.c follows xterm and uses 92 for R and G
198	 components of the bright-blue color, so we bias the divisor a
199	 bit to have the bright colors between 9 and 15 identical to
200	 what ui-style.c expects.  */
201      int bits = rgb[i] / 93;
202      retval |= ((bits > 0) << (2 - i)) | ((bits > 1) << 3);
203    }
204
205  return retval;
206}
207
208/* Zero if not yet initialized, 1 if stdout is a console device, else -1.  */
209static int mingw_console_initialized;
210
211/* Handle to stdout . */
212static HANDLE hstdout = INVALID_HANDLE_VALUE;
213
214/* Text attribute to use for normal text (the "none" pseudo-color).  */
215static SHORT  norm_attr;
216
217/* The most recently applied style.  */
218static ui_file_style last_style;
219
220/* Alternative for the libc 'fputs' which handles embedded SGR
221   sequences in support of styling.  */
222
223int
224gdb_console_fputs (const char *linebuf, FILE *fstream)
225{
226  if (!mingw_console_initialized)
227    {
228      hstdout = (HANDLE)_get_osfhandle (fileno (fstream));
229      DWORD cmode;
230      CONSOLE_SCREEN_BUFFER_INFO csbi;
231
232      if (hstdout != INVALID_HANDLE_VALUE
233	  && GetConsoleMode (hstdout, &cmode) != 0
234	  && GetConsoleScreenBufferInfo (hstdout, &csbi))
235	{
236	  norm_attr = csbi.wAttributes;
237	  mingw_console_initialized = 1;
238	}
239      else if (hstdout != INVALID_HANDLE_VALUE)
240	mingw_console_initialized = -1; /* valid, but not a console device */
241    }
242  /* If our stdout is not a console device, let the default 'fputs'
243     handle the task. */
244  if (mingw_console_initialized <= 0)
245    return 0;
246
247  /* Mapping between 8 ANSI colors and Windows console attributes.  */
248  static int fg_color[] = {
249    0,					/* black */
250    FOREGROUND_RED,			/* red */
251    FOREGROUND_GREEN,			/* green */
252    FOREGROUND_GREEN | FOREGROUND_RED,	/* yellow */
253    FOREGROUND_BLUE,			/* blue */
254    FOREGROUND_BLUE | FOREGROUND_RED,	/* magenta */
255    FOREGROUND_BLUE | FOREGROUND_GREEN, /* cyan */
256    FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE /* gray */
257  };
258  static int bg_color[] = {
259    0,					/* black */
260    BACKGROUND_RED,			/* red */
261    BACKGROUND_GREEN,			/* green */
262    BACKGROUND_GREEN | BACKGROUND_RED,	/* yellow */
263    BACKGROUND_BLUE,			/* blue */
264    BACKGROUND_BLUE | BACKGROUND_RED,	/* magenta */
265    BACKGROUND_BLUE | BACKGROUND_GREEN, /* cyan */
266    BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE /* gray */
267  };
268
269  ui_file_style style = last_style;
270  unsigned char c;
271  size_t n_read;
272
273  for ( ; (c = *linebuf) != 0; linebuf += n_read)
274    {
275      if (c == '\033')
276	{
277	  fflush (fstream);
278	  bool parsed = style.parse (linebuf, &n_read);
279	  if (n_read <= 0)	/* should never happen */
280	    n_read = 1;
281	  if (!parsed)
282	    {
283	      /* This means we silently swallow SGR sequences we
284		 cannot parse.  */
285	      continue;
286	    }
287	  /* Colors.  */
288	  const ui_file_style::color &fg = style.get_foreground ();
289	  const ui_file_style::color &bg = style.get_background ();
290	  int fgcolor, bgcolor, bright, inverse;
291	  if (fg.is_none ())
292	    fgcolor = norm_attr & 15;
293	  else if (fg.is_basic ())
294	    fgcolor = fg_color[fg.get_value () & 15];
295	  else
296	    fgcolor = rgb_to_16colors (fg);
297	  if (bg.is_none ())
298	    bgcolor = norm_attr & (15 << 4);
299	  else if (bg.is_basic ())
300	    bgcolor = bg_color[bg.get_value () & 15];
301	  else
302	    bgcolor = rgb_to_16colors (bg) << 4;
303
304	  /* Intensity.  */
305	  switch (style.get_intensity ())
306	    {
307	    case ui_file_style::NORMAL:
308	    case ui_file_style::DIM:
309	      bright = 0;
310	      break;
311	    case ui_file_style::BOLD:
312	      bright = 1;
313	      break;
314	    default:
315	      gdb_assert_not_reached ("invalid intensity");
316	    }
317
318	  /* Inverse video.  */
319	  if (style.is_reverse ())
320	    inverse = 1;
321	  else
322	    inverse = 0;
323
324	  /* Construct the attribute.  */
325	  if (inverse)
326	    {
327	      int t = fgcolor;
328	      fgcolor = (bgcolor >> 4);
329	      bgcolor = (t << 4);
330	    }
331	  if (bright)
332	    fgcolor |= FOREGROUND_INTENSITY;
333
334	  SHORT attr = (bgcolor & (15 << 4)) | (fgcolor & 15);
335
336	  /* Apply the attribute.  */
337	  SetConsoleTextAttribute (hstdout, attr);
338	}
339      else
340	{
341	  /* When we are about to write newline, we need to clear to
342	     EOL with the normal attribute, to avoid spilling the
343	     colors to the next screen line.  We assume here that no
344	     non-default attribute extends beyond the newline.  */
345	  if (c == '\n')
346	    {
347	      DWORD nchars;
348	      COORD start_pos;
349	      DWORD written;
350	      CONSOLE_SCREEN_BUFFER_INFO csbi;
351
352	      fflush (fstream);
353	      GetConsoleScreenBufferInfo (hstdout, &csbi);
354
355	      if (csbi.wAttributes != norm_attr)
356		{
357		  start_pos = csbi.dwCursorPosition;
358		  nchars = csbi.dwSize.X - start_pos.X;
359
360		  FillConsoleOutputAttribute (hstdout, norm_attr, nchars,
361					      start_pos, &written);
362		  FillConsoleOutputCharacter (hstdout, ' ', nchars,
363					      start_pos, &written);
364		}
365	    }
366	  fputc (c, fstream);
367	  n_read = 1;
368	}
369    }
370
371  last_style = style;
372  return 1;
373}
374