2 * an application for displaying Win32 console
4 * Copyright 2001 Eric Pouech
8 #include "wine/server.h"
9 #include "wine/unicode.h"
10 #include "winecon_private.h"
12 static int trace_level = 1;
13 void XTracer(int level, const char* format, ...)
19 if (level > trace_level) return;
21 va_start(valist, format);
22 len = vsnprintf(buf, sizeof(buf), format, valist);
27 len = sizeof(buf) - 1;
29 buf[len - 1] = buf[len - 2] = buf[len - 3] = '.';
34 /******************************************************************
37 * updates the local copy of cells (band to update)
39 void WINECON_FetchCells(struct inner_data* data, int upd_tp, int upd_bm)
41 SERVER_START_REQ( read_console_output )
43 req->handle = (handle_t)data->hConOut;
46 req->mode = CHAR_INFO_MODE_TEXTATTR;
48 wine_server_set_reply( req, &data->cells[upd_tp * data->curcfg.sb_width],
49 (upd_bm-upd_tp+1) * data->curcfg.sb_width * sizeof(CHAR_INFO) );
50 wine_server_call( req );
53 data->fnRefresh(data, upd_tp, upd_bm);
56 /******************************************************************
57 * WINECON_NotifyWindowChange
59 * Inform server that visible window on sb has changed
61 void WINECON_NotifyWindowChange(struct inner_data* data)
63 SERVER_START_REQ( set_console_output_info )
65 req->handle = (handle_t)data->hConOut;
66 req->win_left = data->curcfg.win_pos.X;
67 req->win_top = data->curcfg.win_pos.Y;
68 req->win_right = data->curcfg.win_pos.X + data->curcfg.win_width - 1;
69 req->win_bottom = data->curcfg.win_pos.Y + data->curcfg.win_height - 1;
70 req->mask = SET_CONSOLE_OUTPUT_INFO_DISPLAY_WINDOW;
71 wine_server_call( req );
76 /******************************************************************
77 * WINECON_GetHistorySize
81 int WINECON_GetHistorySize(HANDLE hConIn)
85 SERVER_START_REQ(get_console_input_info)
87 req->handle = (handle_t)hConIn;
88 if (!wine_server_call_err( req )) ret = reply->history_size;
94 /******************************************************************
95 * WINECON_SetHistorySize
99 BOOL WINECON_SetHistorySize(HANDLE hConIn, int size)
103 SERVER_START_REQ(set_console_input_info)
105 req->handle = (handle_t)hConIn;
106 req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_SIZE;
107 req->history_size = size;
108 ret = !wine_server_call_err( req );
115 /******************************************************************
116 * WINECON_GetHistoryMode
120 int WINECON_GetHistoryMode(HANDLE hConIn)
124 SERVER_START_REQ(get_console_input_info)
126 req->handle = (handle_t)hConIn;
127 if (!wine_server_call_err( req )) ret = reply->history_mode;
133 /******************************************************************
134 * WINECON_SetHistoryMode
138 BOOL WINECON_SetHistoryMode(HANDLE hConIn, int mode)
142 SERVER_START_REQ(set_console_input_info)
144 req->handle = (handle_t)hConIn;
145 req->mask = SET_CONSOLE_INPUT_INFO_HISTORY_MODE;
146 req->history_mode = mode;
147 ret = !wine_server_call_err( req );
153 /******************************************************************
154 * WINECON_GetConsoleTitle
158 BOOL WINECON_GetConsoleTitle(HANDLE hConIn, WCHAR* buffer, size_t len)
162 if (len < sizeof(WCHAR)) return FALSE;
164 SERVER_START_REQ( get_console_input_info )
166 req->handle = (handle_t)hConIn;
167 wine_server_set_reply( req, buffer, len - sizeof(WCHAR) );
168 if ((ret = !wine_server_call_err( req )))
170 len = wine_server_reply_size( reply );
171 buffer[len / sizeof(WCHAR)] = 0;
178 /******************************************************************
179 * WINECON_GrabChanges
181 * A change occurs, try to figure out which
183 int WINECON_GrabChanges(struct inner_data* data)
185 struct console_renderer_event evts[16];
189 SERVER_START_REQ( get_console_renderer_events )
191 wine_server_set_reply( req, evts, sizeof(evts) );
192 req->handle = (handle_t)data->hSynchro;
193 if (!wine_server_call_err( req )) num = wine_server_reply_size(reply) / sizeof(evts[0]);
197 if (!num) {Trace(0, "hmm renderer signaled but no events available\n"); return 1;}
199 /* FIXME: should do some event compression here (cursor pos, update) */
200 Trace(1, "Change notification:");
201 for (i = 0; i < num; i++)
203 switch (evts[i].event)
205 case CONSOLE_RENDERER_TITLE_EVENT:
206 data->fnSetTitle(data);
208 case CONSOLE_RENDERER_ACTIVE_SB_EVENT:
209 SERVER_START_REQ( open_console )
211 req->from = (int)data->hConIn;
212 req->access = GENERIC_READ | GENERIC_WRITE;
213 req->share = FILE_SHARE_READ | FILE_SHARE_WRITE;
214 req->inherit = FALSE;
215 h = wine_server_call_err( req ) ? 0 : (HANDLE)reply->handle;
218 Trace(1, " active(%d)", (int)h);
221 CloseHandle(data->hConOut);
225 case CONSOLE_RENDERER_SB_RESIZE_EVENT:
226 if (data->curcfg.sb_width != evts[i].u.resize.width ||
227 data->curcfg.sb_height != evts[i].u.resize.height)
229 Trace(1, " resize(%d,%d)", evts[i].u.resize.width, evts[i].u.resize.height);
230 data->curcfg.sb_width = evts[i].u.resize.width;
231 data->curcfg.sb_height = evts[i].u.resize.height;
233 data->cells = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, data->cells,
234 data->curcfg.sb_width * data->curcfg.sb_height * sizeof(CHAR_INFO));
235 if (!data->cells) {Trace(0, "OOM\n"); exit(0);}
236 data->fnResizeScreenBuffer(data);
237 data->fnComputePositions(data);
240 case CONSOLE_RENDERER_UPDATE_EVENT:
241 Trace(1, " update(%d,%d)", evts[i].u.update.top, evts[i].u.update.bottom);
242 WINECON_FetchCells(data, evts[i].u.update.top, evts[i].u.update.bottom);
244 case CONSOLE_RENDERER_CURSOR_POS_EVENT:
245 if (evts[i].u.cursor_pos.x != data->cursor.X || evts[i].u.cursor_pos.y != data->cursor.Y)
247 data->cursor.X = evts[i].u.cursor_pos.x;
248 data->cursor.Y = evts[i].u.cursor_pos.y;
249 data->fnPosCursor(data);
250 Trace(1, " curs-pos(%d,%d)",evts[i].u.cursor_pos.x, evts[i].u.cursor_pos.y);
253 case CONSOLE_RENDERER_CURSOR_GEOM_EVENT:
254 if (evts[i].u.cursor_geom.size != data->curcfg.cursor_size ||
255 evts[i].u.cursor_geom.visible != data->curcfg.cursor_visible)
257 data->fnShapeCursor(data, evts[i].u.cursor_geom.size,
258 evts[i].u.cursor_geom.visible, FALSE);
259 Trace(1, " curs-geom(%d,%d)",
260 evts[i].u.cursor_geom.size, evts[i].u.cursor_geom.visible);
263 case CONSOLE_RENDERER_DISPLAY_EVENT:
264 if (evts[i].u.display.left != data->curcfg.win_pos.X)
266 data->fnScroll(data, evts[i].u.display.left, TRUE);
267 data->fnPosCursor(data);
268 Trace(1, " h-scroll(%d)", evts[i].u.display.left);
270 if (evts[i].u.display.top != data->curcfg.win_pos.Y)
272 data->fnScroll(data, evts[i].u.display.top, FALSE);
273 data->fnPosCursor(data);
274 Trace(1, " v-scroll(%d)", evts[i].u.display.top);
276 if (evts[i].u.display.width != data->curcfg.win_width ||
277 evts[i].u.display.height != data->curcfg.win_height)
279 Trace(1, " win-size(%d,%d)", evts[i].u.display.width, evts[i].u.display.height);
280 data->curcfg.win_width = evts[i].u.display.width;
281 data->curcfg.win_height = evts[i].u.display.height;
282 data->fnComputePositions(data);
285 case CONSOLE_RENDERER_EXIT_EVENT:
286 Trace(1, ". Exit!!\n");
289 Trace(0, "Unknown event type (%d)\n", evts[i].event);
293 Trace(1, ". Done\n");
297 /******************************************************************
300 * Destroy wineconsole internal data
302 static void WINECON_Delete(struct inner_data* data)
306 if (data->hConIn) CloseHandle(data->hConIn);
307 if (data->hConOut) CloseHandle(data->hConOut);
308 if (data->hSynchro) CloseHandle(data->hSynchro);
309 if (data->cells) HeapFree(GetProcessHeap(), 0, data->cells);
310 data->fnDeleteBackend(data);
311 HeapFree(GetProcessHeap(), 0, data);
314 /******************************************************************
317 * Initialisation part I. Creation of server object (console input and
318 * active screen buffer)
320 static struct inner_data* WINECON_Init(HINSTANCE hInst, void* pid)
322 struct inner_data* data = NULL;
324 WCHAR szTitle[] = {'W','i','n','e',' ','c','o','n','s','o','l','e',0};
326 data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*data));
329 /* load default registry settings, and copy them into our current configuration */
330 WINECON_RegLoad(&data->defcfg);
331 data->curcfg = data->defcfg;
333 /* the handles here are created without the whistles and bells required by console
334 * (mainly because wineconsole doesn't need it)
335 * - there are not inheritable
336 * - hConIn is not synchronizable
338 SERVER_START_REQ(alloc_console)
340 req->access = GENERIC_READ | GENERIC_WRITE;
341 req->inherit = FALSE;
343 ret = !wine_server_call_err( req );
344 data->hConIn = (HANDLE)reply->handle_in;
345 data->hSynchro = (HANDLE)reply->event;
348 if (!ret) goto error;
350 SERVER_START_REQ( set_console_input_info )
352 req->handle = (handle_t)data->hConIn;
353 req->mask = SET_CONSOLE_INPUT_INFO_TITLE;
354 wine_server_add_data( req, szTitle, strlenW(szTitle) * sizeof(WCHAR) );
355 ret = !wine_server_call_err( req );
358 if (!ret) goto error;
360 SERVER_START_REQ(create_console_output)
362 req->handle_in = (handle_t)data->hConIn;
363 req->access = GENERIC_WRITE|GENERIC_READ;
364 req->share = FILE_SHARE_READ|FILE_SHARE_WRITE;
365 req->inherit = FALSE;
366 data->hConOut = (HANDLE)(wine_server_call_err( req ) ? 0 : reply->handle_out);
369 if (data->hConOut) return data;
372 WINECON_Delete(data);
376 /******************************************************************
379 * Spawn the child processus when invoked with wineconsole foo bar
381 static BOOL WINECON_Spawn(struct inner_data* data, LPCSTR lpCmdLine)
383 PROCESS_INFORMATION info;
385 LPWSTR ptr = GetCommandLine(); /* we're unicode... */
388 /* we're in the case wineconsole <exe> <options>... spawn the new process */
389 memset(&startup, 0, sizeof(startup));
390 startup.cb = sizeof(startup);
391 startup.dwFlags = STARTF_USESTDHANDLES;
393 /* the attributes of wineconsole's handles are not adequate for inheritance, so
394 * get them with the correct attributes before process creation
396 if (!DuplicateHandle(GetCurrentProcess(), data->hConIn, GetCurrentProcess(),
397 &startup.hStdInput, GENERIC_READ|GENERIC_WRITE|SYNCHRONIZE, TRUE, 0) ||
398 !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(),
399 &startup.hStdOutput, GENERIC_READ|GENERIC_WRITE, TRUE, 0) ||
400 !DuplicateHandle(GetCurrentProcess(), data->hConOut, GetCurrentProcess(),
401 &startup.hStdError, GENERIC_READ|GENERIC_WRITE, TRUE, 0))
403 Trace(0, "can't dup handles\n");
404 /* no need to delete handles, we're exiting the programm anyway */
408 /* we could have several ' ' in process command line... so try first space...
410 * the correct way would be to check the existence of the left part of ptr
413 while (*ptr && *ptr++ != ' ');
415 done = *ptr && CreateProcess(NULL, ptr, NULL, NULL, TRUE, 0L, NULL, NULL, &startup, &info);
417 /* we no longer need the handles passed to the child for the console */
418 CloseHandle(startup.hStdInput);
419 CloseHandle(startup.hStdOutput);
420 CloseHandle(startup.hStdError);
425 /******************************************************************
430 static BOOL WINECON_HasEvent(LPCSTR ptr, unsigned *evt)
432 while (*ptr == ' ' || *ptr == '\t') ptr++;
433 if (strncmp(ptr, "--use-event=", 12)) return FALSE;
434 return sscanf(ptr + 12, "%d", evt) == 1;
437 /******************************************************************
440 * wineconsole can either be started as:
441 * wineconsole --use-event=<int> used when a new console is created (AllocConsole)
442 * wineconsole <pgm> <arguments> used to start the program <pgm> from the command line in
443 * a freshly created console
445 int PASCAL WINECON_WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPCSTR lpCmdLine, UINT nCmdShow)
447 struct inner_data* data;
451 /* case of wineconsole <evt>, signal process that created us that we're up and running */
452 if (WINECON_HasEvent(lpCmdLine, &evt))
454 if (!(data = WINECON_Init(hInst, 0))) return 0;
455 ret = SetEvent((HANDLE)evt);
459 if (!(data = WINECON_Init(hInst, (void*)GetCurrentProcessId()))) return 0;
460 ret = WINECON_Spawn(data, lpCmdLine);
463 if (ret && WCUSER_InitBackend(data))
465 ret = data->fnMainLoop(data);
467 WINECON_Delete(data);