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