Commit | Line | Data |
---|---|---|
121ce6e5 DJ |
1 | /* Host support routines for MinGW, for GDB, the GNU debugger. |
2 | ||
b811d2c2 | 3 | Copyright (C) 2006-2020 Free Software Foundation, Inc. |
121ce6e5 DJ |
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 | |
a9762ec7 | 9 | the Free Software Foundation; either version 3 of the License, or |
121ce6e5 DJ |
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 | |
a9762ec7 | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
121ce6e5 DJ |
19 | |
20 | #include "defs.h" | |
d9ac0664 | 21 | #include "main.h" |
0ea3f30e | 22 | #include "serial.h" |
400b5eca | 23 | #include "gdbsupport/event-loop.h" |
121ce6e5 | 24 | |
06cc9596 | 25 | #include "gdbsupport/gdb_select.h" |
121ce6e5 DJ |
26 | |
27 | #include <windows.h> | |
28 | ||
d9ac0664 EZ |
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 | ||
32 | char * | |
33 | windows_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 | ||
0ea3f30e DJ |
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 | ||
49 | int | |
50 | gdb_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; | |
4577549b DJ |
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; | |
0ea3f30e DJ |
63 | int fd; |
64 | int num_ready; | |
4577549b | 65 | size_t indx; |
0ea3f30e | 66 | |
16d01f9c BW |
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 | ||
0ea3f30e DJ |
78 | num_ready = 0; |
79 | num_handles = 0; | |
4577549b | 80 | num_scbs = 0; |
0ea3f30e DJ |
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 | ||
98739726 DJ |
91 | if ((!readfds || !FD_ISSET (fd, readfds)) |
92 | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | |
0ea3f30e | 93 | continue; |
0ea3f30e DJ |
94 | |
95 | scb = serial_for_fd (fd); | |
96 | if (scb) | |
4577549b DJ |
97 | { |
98 | serial_wait_handle (scb, &read, &except); | |
99 | scbs[num_scbs++] = scb; | |
100 | } | |
0ea3f30e DJ |
101 | |
102 | if (read == NULL) | |
4577549b | 103 | read = (HANDLE) _get_osfhandle (fd); |
0ea3f30e DJ |
104 | if (except == NULL) |
105 | { | |
106 | if (!never_handle) | |
107 | never_handle = CreateEvent (0, FALSE, FALSE, 0); | |
108 | ||
109 | except = never_handle; | |
110 | } | |
111 | ||
98739726 | 112 | if (readfds && FD_ISSET (fd, readfds)) |
0ea3f30e DJ |
113 | { |
114 | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | |
115 | handles[num_handles++] = read; | |
116 | } | |
117 | ||
98739726 | 118 | if (exceptfds && FD_ISSET (fd, exceptfds)) |
0ea3f30e DJ |
119 | { |
120 | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | |
121 | handles[num_handles++] = except; | |
122 | } | |
123 | } | |
0ea3f30e | 124 | |
585a46a2 | 125 | gdb_assert (num_handles <= MAXIMUM_WAIT_OBJECTS); |
0ea3f30e DJ |
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)); | |
4577549b DJ |
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]); | |
0ea3f30e DJ |
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; | |
c3e2b812 | 152 | |
98739726 DJ |
153 | if ((!readfds || !FD_ISSET (fd, readfds)) |
154 | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | |
c3e2b812 | 155 | continue; |
0ea3f30e | 156 | |
98739726 | 157 | if (readfds && FD_ISSET (fd, readfds)) |
0ea3f30e DJ |
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 | ||
98739726 | 168 | if (exceptfds && FD_ISSET (fd, exceptfds)) |
0ea3f30e DJ |
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 | } | |
e4adb939 EZ |
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. */ | |
186 | static int | |
187 | rgb_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. */ | |
209 | static int mingw_console_initialized; | |
210 | ||
211 | /* Handle to stdout . */ | |
212 | static HANDLE hstdout = INVALID_HANDLE_VALUE; | |
213 | ||
214 | /* Text attribute to use for normal text (the "none" pseudo-color). */ | |
215 | static SHORT norm_attr; | |
216 | ||
217 | /* The most recently applied style. */ | |
218 | static ui_file_style last_style; | |
219 | ||
220 | /* Alternative for the libc 'fputs' which handles embedded SGR | |
221 | sequences in support of styling. */ | |
222 | ||
223 | int | |
224 | gdb_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 | } |