]>
Commit | Line | Data |
---|---|---|
121ce6e5 DJ |
1 | /* Host support routines for MinGW, for GDB, the GNU debugger. |
2 | ||
42a4f53d | 3 | Copyright (C) 2006-2019 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" |
b803fb0f | 23 | #include "event-loop.h" |
121ce6e5 | 24 | |
0ea3f30e | 25 | #include "gdb_select.h" |
b803fb0f | 26 | #include "readline/readline.h" |
121ce6e5 DJ |
27 | |
28 | #include <windows.h> | |
29 | ||
d9ac0664 EZ |
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 | ||
33 | char * | |
34 | windows_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 | ||
0ea3f30e DJ |
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 | ||
50 | int | |
51 | gdb_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; | |
4577549b DJ |
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; | |
0ea3f30e DJ |
64 | int fd; |
65 | int num_ready; | |
4577549b | 66 | size_t indx; |
0ea3f30e DJ |
67 | |
68 | num_ready = 0; | |
69 | num_handles = 0; | |
4577549b | 70 | num_scbs = 0; |
0ea3f30e DJ |
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 | ||
98739726 DJ |
81 | if ((!readfds || !FD_ISSET (fd, readfds)) |
82 | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | |
0ea3f30e | 83 | continue; |
0ea3f30e DJ |
84 | |
85 | scb = serial_for_fd (fd); | |
86 | if (scb) | |
4577549b DJ |
87 | { |
88 | serial_wait_handle (scb, &read, &except); | |
89 | scbs[num_scbs++] = scb; | |
90 | } | |
0ea3f30e DJ |
91 | |
92 | if (read == NULL) | |
4577549b | 93 | read = (HANDLE) _get_osfhandle (fd); |
0ea3f30e DJ |
94 | if (except == NULL) |
95 | { | |
96 | if (!never_handle) | |
97 | never_handle = CreateEvent (0, FALSE, FALSE, 0); | |
98 | ||
99 | except = never_handle; | |
100 | } | |
101 | ||
98739726 | 102 | if (readfds && FD_ISSET (fd, readfds)) |
0ea3f30e DJ |
103 | { |
104 | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | |
105 | handles[num_handles++] = read; | |
106 | } | |
107 | ||
98739726 | 108 | if (exceptfds && FD_ISSET (fd, exceptfds)) |
0ea3f30e DJ |
109 | { |
110 | gdb_assert (num_handles < MAXIMUM_WAIT_OBJECTS); | |
111 | handles[num_handles++] = except; | |
112 | } | |
113 | } | |
0ea3f30e | 114 | |
585a46a2 | 115 | gdb_assert (num_handles <= MAXIMUM_WAIT_OBJECTS); |
0ea3f30e DJ |
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)); | |
4577549b DJ |
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]); | |
0ea3f30e DJ |
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; | |
c3e2b812 | 142 | |
98739726 DJ |
143 | if ((!readfds || !FD_ISSET (fd, readfds)) |
144 | && (!exceptfds || !FD_ISSET (fd, exceptfds))) | |
c3e2b812 | 145 | continue; |
0ea3f30e | 146 | |
98739726 | 147 | if (readfds && FD_ISSET (fd, readfds)) |
0ea3f30e DJ |
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 | ||
98739726 | 158 | if (exceptfds && FD_ISSET (fd, exceptfds)) |
0ea3f30e DJ |
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 | ||
b803fb0f DJ |
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 | ||
0ea3f30e DJ |
178 | return num_ready; |
179 | } | |
e4adb939 EZ |
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. */ | |
184 | static int | |
185 | rgb_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. */ | |
207 | static int mingw_console_initialized; | |
208 | ||
209 | /* Handle to stdout . */ | |
210 | static HANDLE hstdout = INVALID_HANDLE_VALUE; | |
211 | ||
212 | /* Text attribute to use for normal text (the "none" pseudo-color). */ | |
213 | static SHORT norm_attr; | |
214 | ||
215 | /* The most recently applied style. */ | |
216 | static ui_file_style last_style; | |
217 | ||
218 | /* Alternative for the libc 'fputs' which handles embedded SGR | |
219 | sequences in support of styling. */ | |
220 | ||
221 | int | |
222 | gdb_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 | } |