|
|
1.1 root 1: /*
2: Hatari - control.c
3:
1.1.1.7 root 4: This file is distributed under the GNU General Public License, version 2
5: or at your option any later version. Read the file gpl.txt for details.
1.1 root 6:
7: This code processes commands from the Hatari control socket
8: */
1.1.1.2 root 9: const char Control_fileid[] = "Hatari control.c : " __DATE__ " " __TIME__;
1.1 root 10:
11: #include "config.h"
12:
1.1.1.3 root 13: #if HAVE_UNIX_DOMAIN_SOCKETS
1.1 root 14: # include <sys/socket.h>
1.1.1.12! root 15: # include <sys/stat.h> /* mkfifo() */
1.1.1.3 root 16: # include <sys/un.h>
1.1.1.12! root 17: # include <fcntl.h>
1.1.1.3 root 18: #endif
1.1 root 19:
20: #include <sys/types.h>
1.1.1.10 root 21: #if HAVE_SYS_TIME_H
1.1 root 22: #include <sys/time.h>
1.1.1.10 root 23: #endif
1.1 root 24: #include <unistd.h>
25: #include <ctype.h>
1.1.1.8 root 26: #include <assert.h>
1.1 root 27:
28: #include "main.h"
29: #include "change.h"
30: #include "configuration.h"
31: #include "control.h"
32: #include "debugui.h"
33: #include "file.h"
34: #include "ikbd.h"
35: #include "keymap.h"
36: #include "log.h"
37: #include "midi.h"
38: #include "printer.h"
39: #include "rs232.h"
1.1.1.12! root 40: #include "scc.h"
1.1 root 41: #include "shortcut.h"
42: #include "str.h"
1.1.1.9 root 43: #include "screen.h"
1.1 root 44:
45: typedef enum {
46: DO_DISABLE,
47: DO_ENABLE,
48: DO_TOGGLE
49: } action_t;
50:
51: /* Whether to send embedded window info */
52: static bool bSendEmbedInfo;
53: /* Pausing triggered remotely (battery save pause) */
54: static bool bRemotePaused;
55:
56:
57: /*-----------------------------------------------------------------------*/
58: /**
59: * Parse key command and synthetize key press/release
60: * corresponding to given keycode or character.
1.1.1.3 root 61: * Return false if parsing failed, true otherwise
1.1 root 62: *
63: * This can be used by external Hatari UI(s) for
64: * string macros, or on devices which lack keyboard
65: */
66: static bool Control_InsertKey(const char *event)
67: {
68: const char *key = NULL;
1.1.1.5 root 69: bool up, down;
1.1 root 70:
71: if (strncmp(event, "keypress ", 9) == 0) {
72: key = &event[9];
1.1.1.5 root 73: down = up = true;
74: } else if (strncmp(event, "keydown ", 8) == 0) {
75: key = &event[8];
76: down = true;
77: up = false;
78: } else if (strncmp(event, "keyup ", 6) == 0) {
79: key = &event[6];
80: down = false;
81: up = true;
1.1 root 82: }
83: if (!(key && key[0])) {
1.1.1.5 root 84: fprintf(stderr, "ERROR: '%s' contains no key press/down/up event\n", event);
1.1.1.3 root 85: return false;
1.1 root 86: }
87: if (key[1]) {
88: char *endptr;
89: /* multiple characters, assume it's a keycode */
90: int keycode = strtol(key, &endptr, 0);
91: /* not a valid number or keycode is out of range? */
92: if (*endptr || keycode < 0 || keycode > 255) {
1.1.1.6 root 93: fprintf(stderr, "ERROR: '%s' isn't a valid key scancode, got value %d\n",
1.1 root 94: key, keycode);
1.1.1.3 root 95: return false;
1.1 root 96: }
1.1.1.5 root 97: if (down) {
98: IKBD_PressSTKey(keycode, true);
99: }
100: if (up) {
101: IKBD_PressSTKey(keycode, false);
102: }
1.1 root 103: } else {
1.1.1.8 root 104: if (!isalnum((unsigned char)key[0])) {
1.1.1.6 root 105: fprintf(stderr, "ERROR: non-alphanumeric character '%c' needs to be given as keycode\n", key[0]);
106: return false;
107: }
1.1.1.5 root 108: if (down) {
109: Keymap_SimulateCharacter(key[0], true);
110: }
111: if (up) {
112: Keymap_SimulateCharacter(key[0], false);
113: }
1.1 root 114: }
1.1.1.5 root 115: #if 0
116: fprintf(stderr, "Simulated key %s of %d\n",
117: (down? (up? "press":"down") :"up"), key);
118: #endif
1.1.1.3 root 119: return true;
1.1 root 120: }
121:
122: /*-----------------------------------------------------------------------*/
123: /**
124: * Parse event name and synthetize corresponding event to emulation
1.1.1.3 root 125: * Return false if name parsing failed, true otherwise
1.1 root 126: *
127: * This can be used by external Hatari UI(s) on devices which input
128: * methods differ from normal keyboard and mouse, such as high DPI
129: * touchscreen (no right/middle button, inaccurate clicks)
130: */
131: static bool Control_InsertEvent(const char *event)
132: {
133: if (strcmp(event, "doubleclick") == 0) {
134: Keyboard.LButtonDblClk = 1;
1.1.1.3 root 135: return true;
1.1 root 136: }
1.1.1.5 root 137: if (strcmp(event, "rightdown") == 0) {
1.1 root 138: Keyboard.bRButtonDown |= BUTTON_MOUSE;
1.1.1.3 root 139: return true;
1.1 root 140: }
1.1.1.5 root 141: if (strcmp(event, "rightup") == 0) {
1.1 root 142: Keyboard.bRButtonDown &= ~BUTTON_MOUSE;
1.1.1.3 root 143: return true;
1.1 root 144: }
145: if (Control_InsertKey(event)) {
1.1.1.3 root 146: return true;
1.1 root 147: }
148: fprintf(stderr, "ERROR: unrecognized event: '%s'\n", event);
1.1.1.5 root 149: fprintf(stderr,
150: "Supported mouse button and key events are:\n"
151: "- doubleclick\n"
152: "- rightdown\n"
153: "- rightup\n"
154: "- keypress <key>\n"
155: "- keydown <key>\n"
156: "- keyup <key>\n"
157: "<key> can be either a single ASCII character or an ST scancode\n"
158: "(e.g. space has scancode of 57 and enter 28).\n"
159: );
1.1.1.3 root 160: return false;
1.1 root 161: }
162:
163: /*-----------------------------------------------------------------------*/
164: /**
165: * Parse device name and enable/disable/toggle & init/uninit it according
1.1.1.3 root 166: * to action. Return false if name parsing failed, true otherwise
1.1 root 167: */
168: static bool Control_DeviceAction(const char *name, action_t action)
169: {
170: /* Note: e.g. RTC would require restarting emulation
171: * and HD-boot setting emulation reboot. Devices
172: * listed here work just with init/uninit.
173: */
174: struct {
175: const char *name;
176: bool *pvalue;
177: void(*init)(void);
178: void(*uninit)(void);
179: } item[] = {
180: { "printer", &ConfigureParams.Printer.bEnablePrinting, Printer_Init, Printer_UnInit },
181: { "rs232", &ConfigureParams.RS232.bEnableRS232, RS232_Init, RS232_UnInit },
1.1.1.12! root 182: { "sccb", &ConfigureParams.RS232.bEnableSccB, SCC_Init, SCC_UnInit },
1.1 root 183: { "midi", &ConfigureParams.Midi.bEnableMidi, Midi_Init, Midi_UnInit },
184: { NULL, NULL, NULL, NULL }
185: };
186: int i;
187: bool value;
188: for (i = 0; item[i].name; i++)
189: {
190: if (strcmp(name, item[i].name) == 0)
191: {
192: switch (action) {
193: case DO_TOGGLE:
194: value = !*(item[i].pvalue);
195: break;
196: case DO_ENABLE:
1.1.1.3 root 197: value = true;
1.1 root 198: break;
199: case DO_DISABLE:
200: default:
1.1.1.3 root 201: value = false;
1.1 root 202: break;
203: }
204: *(item[i].pvalue) = value;
205: if (value) {
206: item[i].init();
207: } else {
208: item[i].uninit();
209: }
210: fprintf(stderr, "%s: %s\n", name, value?"ON":"OFF");
1.1.1.3 root 211: return true;
1.1 root 212: }
213: }
214: fprintf(stderr, "WARNING: unknown device '%s'\n\n", name);
215: fprintf(stderr, "Accepted devices are:\n");
216: for (i = 0; item[i].name; i++)
217: {
218: fprintf(stderr, "- %s\n", item[i].name);
219: }
1.1.1.3 root 220: return false;
1.1 root 221: }
222:
223: /*-----------------------------------------------------------------------*/
224: /**
225: * Parse path type name and set the path to given value.
1.1.1.3 root 226: * Return false if name parsing failed, true otherwise
1.1 root 227: */
228: static bool Control_SetPath(char *name)
229: {
230: struct {
231: const char *name;
232: char *path;
233: } item[] = {
234: { "memauto", ConfigureParams.Memory.szAutoSaveFileName },
235: { "memsave", ConfigureParams.Memory.szMemoryCaptureFileName },
1.1.1.2 root 236: { "midiin", ConfigureParams.Midi.sMidiInFileName },
237: { "midiout", ConfigureParams.Midi.sMidiOutFileName },
1.1 root 238: { "printout", ConfigureParams.Printer.szPrintToFileName },
239: { "soundout", ConfigureParams.Sound.szYMCaptureFileName },
240: { "rs232in", ConfigureParams.RS232.szInFileName },
241: { "rs232out", ConfigureParams.RS232.szOutFileName },
1.1.1.12! root 242: // { "sccbin", ConfigureParams.RS232.sSccBInFileName },
! 243: { "sccbout", ConfigureParams.RS232.sSccBOutFileName },
1.1 root 244: { NULL, NULL }
245: };
246: int i;
247: char *arg;
248: const char *value;
249:
250: /* argument? */
251: arg = strchr(name, ' ');
252: if (arg) {
253: *arg = '\0';
254: value = Str_Trim(arg+1);
255: } else {
1.1.1.3 root 256: return false;
1.1 root 257: }
258:
259: for (i = 0; item[i].name; i++)
260: {
261: if (strcmp(name, item[i].name) == 0)
262: {
263: fprintf(stderr, "%s: %s -> %s\n", name, item[i].path, value);
264: strncpy(item[i].path, value, FILENAME_MAX-1);
1.1.1.3 root 265: return true;
1.1 root 266: }
267: }
268: fprintf(stderr, "WARNING: unknown path type '%s'\n\n", name);
269: fprintf(stderr, "Accepted paths types are:\n");
270: for (i = 0; item[i].name; i++)
271: {
272: fprintf(stderr, "- %s\n", item[i].name);
273: }
1.1.1.3 root 274: return false;
1.1 root 275: }
276:
277: /*-----------------------------------------------------------------------*/
278: /**
1.1.1.3 root 279: * Show Hatari remote usage info and return false
1.1 root 280: */
281: static bool Control_Usage(const char *cmd)
282: {
1.1.1.8 root 283: fprintf(stderr, "ERROR: unrecognized hatari command: '%s'!\n", cmd);
1.1 root 284: fprintf(stderr,
285: "Supported commands are:\n"
286: "- hatari-debug <Debug UI command>\n"
287: "- hatari-event <event to simulate>\n"
288: "- hatari-option <command line options>\n"
289: "- hatari-enable/disable/toggle <device name>\n"
290: "- hatari-path <config name> <new path>\n"
291: "- hatari-shortcut <shortcut name>\n"
292: "- hatari-embed-info\n"
293: "- hatari-stop\n"
294: "- hatari-cont\n"
295: "The last two can be used to stop and continue the Hatari emulation.\n"
1.1.1.6 root 296: "All commands need to be separated by newlines. Spaces in command\n"
297: "line option arguments need to be quoted with \\.\n"
1.1 root 298: );
1.1.1.3 root 299: return false;
1.1 root 300: }
301:
302: /*-----------------------------------------------------------------------*/
303: /**
304: * Parse Hatari debug/event/option/toggle/path/shortcut command buffer.
305: */
1.1.1.8 root 306: void Control_ProcessBuffer(const char *orig)
1.1 root 307: {
1.1.1.8 root 308: char *cmd, *cmdend, *arg, *buffer;
1.1.1.3 root 309: int ok = true;
1.1.1.8 root 310:
311: /* this is called from several different places,
312: * so take a copy of the original buffer so
313: * that it can be sliced & diced
314: */
315: buffer = strdup(orig);
316: assert(buffer);
317:
1.1 root 318: cmd = buffer;
319: do {
320: /* command terminator? */
321: cmdend = strchr(cmd, '\n');
322: if (cmdend) {
323: *cmdend = '\0';
324: }
325: /* arguments? */
326: arg = strchr(cmd, ' ');
327: if (arg) {
328: *arg = '\0';
329: arg = Str_Trim(arg+1);
330: }
331: if (arg) {
332: if (strcmp(cmd, "hatari-option") == 0) {
333: ok = Change_ApplyCommandline(arg);
334: } else if (strcmp(cmd, "hatari-debug") == 0) {
1.1.1.7 root 335: ok = DebugUI_ParseLine(arg);
1.1 root 336: } else if (strcmp(cmd, "hatari-shortcut") == 0) {
337: ok = Shortcut_Invoke(arg);
338: } else if (strcmp(cmd, "hatari-event") == 0) {
339: ok = Control_InsertEvent(arg);
340: } else if (strcmp(cmd, "hatari-path") == 0) {
341: ok = Control_SetPath(arg);
342: } else if (strcmp(cmd, "hatari-enable") == 0) {
343: ok = Control_DeviceAction(arg, DO_ENABLE);
344: } else if (strcmp(cmd, "hatari-disable") == 0) {
345: ok = Control_DeviceAction(arg, DO_DISABLE);
346: } else if (strcmp(cmd, "hatari-toggle") == 0) {
347: ok = Control_DeviceAction(arg, DO_TOGGLE);
348: } else {
349: ok = Control_Usage(cmd);
350: }
351: } else {
352: if (strcmp(cmd, "hatari-embed-info") == 0) {
353: fprintf(stderr, "Embedded window ID change messages = ON\n");
1.1.1.3 root 354: bSendEmbedInfo = true;
1.1 root 355: } else if (strcmp(cmd, "hatari-stop") == 0) {
1.1.1.3 root 356: Main_PauseEmulation(true);
357: bRemotePaused = true;
1.1 root 358: } else if (strcmp(cmd, "hatari-cont") == 0) {
359: Main_UnPauseEmulation();
1.1.1.3 root 360: bRemotePaused = false;
1.1 root 361: } else {
362: ok = Control_Usage(cmd);
363: }
364: }
365: if (cmdend) {
366: cmd = cmdend + 1;
367: }
368: } while (ok && cmdend && *cmd);
1.1.1.8 root 369: free(buffer);
1.1 root 370: }
371:
372:
1.1.1.3 root 373: #if HAVE_UNIX_DOMAIN_SOCKETS
374:
1.1.1.12! root 375: /* one-way fifo which Hatari creates and reads commands from */
! 376: static char *FifoPath;
! 377: static int ControlFifo;
! 378:
! 379: /* two-way socket to which Hatari connects, reads control commands
! 380: * from, and where the command responses (if any) are written to
! 381: */
1.1.1.3 root 382: static int ControlSocket;
383:
384: /* pre-declared local functions */
385: static int Control_GetUISocket(void);
386:
387:
1.1 root 388: /*-----------------------------------------------------------------------*/
389: /**
390: * Check ControlSocket for new commands and execute them.
391: * Commands should be separated by newlines.
392: *
1.1.1.3 root 393: * Return true if remote pause ON (and connected), false otherwise
1.1 root 394: */
395: bool Control_CheckUpdates(void)
396: {
397: /* just using all trace options with +/- are about 300 chars */
398: char buffer[400];
399: struct timeval tv;
400: fd_set readfds;
401: ssize_t bytes;
402: int status, sock;
403:
1.1.1.12! root 404: if (ControlFifo) {
! 405: /* assume whole command can be read in one go */
! 406: bytes = read(ControlFifo, buffer, sizeof(buffer)-1);
! 407: if (bytes < 0) {
! 408: perror("command FIFO read error");
! 409: return false;
! 410: }
! 411: if (bytes == 0) {
! 412: /* non-blocking read, nothing to read */
! 413: return false;
! 414: }
! 415: buffer[bytes] = '\0';
! 416: Control_ProcessBuffer(buffer);
! 417: return false;
! 418: }
! 419:
1.1 root 420: /* socket of file? */
421: if (ControlSocket) {
422: sock = ControlSocket;
423: } else {
1.1.1.3 root 424: return false;
1.1 root 425: }
426:
427: /* ready for reading? */
428: tv.tv_usec = tv.tv_sec = 0;
429: do {
430: FD_ZERO(&readfds);
431: FD_SET(sock, &readfds);
432: if (bRemotePaused) {
433: /* return only when there're UI events
434: * (redraws etc) to save battery:
435: * http://bugzilla.libsdl.org/show_bug.cgi?id=323
436: */
437: int uisock = Control_GetUISocket();
438: if (uisock) {
439: FD_SET(uisock, &readfds);
440: if (uisock < sock) {
441: uisock = sock;
442: }
443: }
444: status = select(uisock+1, &readfds, NULL, NULL, NULL);
445: } else {
446: status = select(sock+1, &readfds, NULL, NULL, &tv);
447: }
448: if (status < 0) {
449: perror("Control socket select() error");
1.1.1.3 root 450: return false;
1.1 root 451: }
452: /* nothing to process here */
453: if (status == 0) {
454: return bRemotePaused;
455: }
456: if (!FD_ISSET(sock, &readfds)) {
457: return bRemotePaused;
458: }
459:
460: /* assume whole command can be read in one go */
461: bytes = read(sock, buffer, sizeof(buffer)-1);
1.1.1.12! root 462: if (bytes < 0) {
! 463: perror("Control socket read error");
1.1.1.3 root 464: return false;
1.1 root 465: }
466: if (bytes == 0) {
467: /* closed */
1.1.1.12! root 468: fprintf(stderr, "ready control socket with 0 bytes available -> close socket\n");
1.1 root 469: close(ControlSocket);
470: ControlSocket = 0;
1.1.1.3 root 471: return false;
1.1 root 472: }
473: buffer[bytes] = '\0';
474: Control_ProcessBuffer(buffer);
475:
476: } while (bRemotePaused);
477:
1.1.1.3 root 478: return false;
1.1 root 479: }
480:
481:
482: /*-----------------------------------------------------------------------*/
483: /**
1.1.1.12! root 484: * Close and remove FIFO file
! 485: */
! 486: void Control_RemoveFifo(void)
! 487: {
! 488: if (ControlFifo) {
! 489: close(ControlFifo);
! 490: ControlFifo = 0;
! 491: }
! 492: if (FifoPath) {
! 493: Log_Printf(LOG_DEBUG, "removing command FIFO: %s\n", FifoPath);
! 494: if (remove(FifoPath) < 0)
! 495: {
! 496: perror("Remove FIFO failed");
! 497: }
! 498: free(FifoPath);
! 499: FifoPath = NULL;
! 500: }
! 501: }
! 502:
! 503: /*-----------------------------------------------------------------------*/
! 504: /**
! 505: * Open given command FIFO
! 506: * Return NULL for success, otherwise an error string
! 507: */
! 508: const char *Control_SetFifo(const char *path)
! 509: {
! 510: int fifo;
! 511:
! 512: if (ControlSocket) {
! 513: return "Can't use a FIFO at the same time with a control socket";
! 514: }
! 515:
! 516: Control_RemoveFifo();
! 517: Log_Printf(LOG_DEBUG, "creating command FIFO: %s\n", path);
! 518:
! 519: if (mkfifo(path, S_IRUSR | S_IWUSR)) {
! 520: perror("FIFO creation error");
! 521: return "Can't create FIFO file";
! 522: }
! 523: FifoPath = strdup(path);
! 524:
! 525: fifo = open(path, O_RDONLY | O_NONBLOCK);
! 526: if (fifo < 0) {
! 527: perror("FIFO open error");
! 528: Control_RemoveFifo();
! 529: return "opening non-blocking read-only FIFO failed";
! 530: }
! 531: ControlFifo = fifo;
! 532: return NULL;
! 533: }
! 534:
! 535:
! 536: /*-----------------------------------------------------------------------*/
! 537: /**
1.1 root 538: * Open given control socket.
539: * Return NULL for success, otherwise an error string
540: */
541: const char *Control_SetSocket(const char *socketpath)
542: {
543: struct sockaddr_un address;
544: int newsock;
1.1.1.12! root 545:
! 546: if (ControlFifo) {
! 547: return "Can't use a FIFO at the same time with a control socket";
! 548: }
1.1 root 549:
550: newsock = socket(AF_UNIX, SOCK_STREAM, 0);
1.1.1.12! root 551: if (newsock < 0) {
! 552: perror("socket creation error");
1.1 root 553: return "Can't create AF_UNIX socket";
554: }
555:
556: address.sun_family = AF_UNIX;
557: strncpy(address.sun_path, socketpath, sizeof(address.sun_path));
558: address.sun_path[sizeof(address.sun_path)-1] = '\0';
559: Log_Printf(LOG_INFO, "Connecting to control socket '%s'...\n", address.sun_path);
1.1.1.12! root 560: if (connect(newsock, (struct sockaddr *)&address, sizeof(address)) < 0) {
! 561: perror("socket connect error");
1.1 root 562: close(newsock);
563: return "connection to control socket failed";
564: }
565:
566: if (ControlSocket) {
567: close(ControlSocket);
568: }
569: ControlSocket = newsock;
570: Log_Printf(LOG_INFO, "new control socket is '%s'\n", socketpath);
571: return NULL;
572: }
573:
574:
575: /*-----------------------------------------------------------------------
576: * Currently works only on X11.
577: *
578: * SDL_syswm.h automatically includes everything else needed.
579: */
580:
1.1.1.9 root 581: #if HAVE_SDL_CONFIG_H
1.1.1.3 root 582: #include <SDL_config.h>
583: #endif
584:
585: /* X11 available and SDL_config.h states that SDL supports X11 */
586: #if HAVE_X11 && SDL_VIDEO_DRIVER_X11
1.1 root 587: #include <SDL_syswm.h>
588:
1.1.1.9 root 589: #if WITH_SDL2
590: #define SDL_GetWMInfo(inf) SDL_GetWindowWMInfo(sdlWindow, inf)
591: #endif
592:
1.1 root 593: /**
594: * Reparent Hatari window if so requested. Needs to be done inside
595: * Hatari because if SDL itself is requested to reparent itself,
596: * SDL window stops accepting any input (specifically done like
597: * this in SDL backends for some reason).
598: *
599: * 'noembed' argument tells whether the SDL window should be embedded
600: * or not.
601: *
602: * If the window is embedded (which means that SDL WM window needs
603: * to be hidden) when SDL is asked to fullscreen, Hatari window just
604: * disappears when returning back from fullscreen. I.e. call this
1.1.1.3 root 605: * with noembed=true _before_ fullscreening and any other time with
606: * noembed=false after changing window size. You can do this by
1.1 root 607: * giving bInFullscreen as the noembed value.
608: */
609: void Control_ReparentWindow(int width, int height, bool noembed)
610: {
611: Display *display;
1.1.1.9 root 612: Window parent_win, sdl_win;
1.1 root 613: const char *parent_win_id;
614: SDL_SysWMinfo info;
1.1.1.9 root 615: Window wm_win;
1.1.1.11 root 616: #if WITH_SDL2
617: Window dw1, *dw2;
618: unsigned int nwin;
1.1.1.9 root 619: #endif
1.1 root 620:
621: parent_win_id = getenv("PARENT_WIN_ID");
622: if (!parent_win_id) {
623: return;
624: }
625: parent_win = strtol(parent_win_id, NULL, 0);
626: if (!parent_win) {
627: Log_Printf(LOG_WARN, "Invalid PARENT_WIN_ID value '%s'\n", parent_win_id);
628: return;
629: }
630:
631: SDL_VERSION(&info.version);
632: if (!SDL_GetWMInfo(&info)) {
633: Log_Printf(LOG_WARN, "Failed to get SDL_GetWMInfo()\n");
634: return;
635: }
1.1.1.11 root 636:
1.1 root 637: display = info.info.x11.display;
638: sdl_win = info.info.x11.window;
1.1.1.9 root 639: #if !WITH_SDL2
1.1 root 640: wm_win = info.info.x11.wmwindow;
641: info.info.x11.lock_func();
1.1.1.11 root 642: #else
643: XQueryTree(display, sdl_win, &dw1, &wm_win, &dw2, &nwin);
1.1.1.9 root 644: #endif
645: if (noembed)
646: {
1.1 root 647: /* show WM window again */
648: XMapWindow(display, wm_win);
1.1.1.9 root 649: }
650: else
651: {
1.1 root 652: char buffer[12]; /* 32-bits in hex (+ '\r') + '\n' + '\0' */
653:
654: /* hide WM window for Hatari */
655: XUnmapWindow(display, wm_win);
1.1.1.11 root 656:
1.1 root 657: /* reparent main Hatari window to given parent */
658: XReparentWindow(display, sdl_win, parent_win, 0, 0);
659:
660: /* whether to send new window size */
1.1.1.9 root 661: if (bSendEmbedInfo && ControlSocket)
662: {
1.1.1.12! root 663: Log_Printf(LOG_INFO, "New %dx%d SDL window with ID: %lx\n",
1.1 root 664: width, height, sdl_win);
665: sprintf(buffer, "%dx%d", width, height);
1.1.1.3 root 666: if (write(ControlSocket, buffer, strlen(buffer)) < 0)
1.1.1.12! root 667: perror("Control_ReparentWindow write error");
1.1 root 668: }
669: }
1.1.1.11 root 670: #if WITH_SDL2
671: XSync(display, false);
672: #else
1.1 root 673: info.info.x11.unlock_func();
1.1.1.9 root 674: #endif
1.1 root 675: }
676:
677: /**
678: * Return the X connection socket or zero
679: */
680: static int Control_GetUISocket(void)
681: {
682: SDL_SysWMinfo info;
683: SDL_VERSION(&info.version);
684: if (!SDL_GetWMInfo(&info)) {
685: Log_Printf(LOG_WARN, "Failed to get SDL_GetWMInfo()\n");
686: return 0;
687: }
688: return ConnectionNumber(info.info.x11.display);
689: }
690:
1.1.1.3 root 691: #else /* HAVE_X11 */
692:
1.1 root 693: static int Control_GetUISocket(void)
694: {
695: return 0;
696: }
697: void Control_ReparentWindow(int width, int height, bool noembed)
698: {
699: /* TODO: implement the Windows part. SDL sources offer example */
700: Log_Printf(LOG_TODO, "Support for Hatari window reparenting not built in\n");
701: }
702:
1.1.1.3 root 703: #endif /* HAVE_X11 */
1.1 root 704:
705: #endif /* HAVE_UNIX_DOMAIN_SOCKETS */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.