/* * XBrlAPI - A background process tinkering with X for proper BrlAPI behavior * * Copyright (C) 2003-2019 by Samuel Thibault * * XBrlAPI comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ /* Compile with: * gcc -O3 -Wall xbrlapi.c -L/usr/X11R6/lib -lbrlapi -lX11 -o xbrlapi */ #include "prologue.h" #include #include #include #include #ifdef HAVE_LANGINFO_H #include #endif /* HAVE_LANGINFO_H */ #ifdef HAVE_SYS_SELECT_H #include #else /* HAVE_SYS_SELECT_H */ #include #endif /* HAVE_SYS_SELECT_H */ #ifdef HAVE_ICONV_H #include #endif /* HAVE_ICONV_H */ #include #include #include #include #include #undef CAN_SIMULATE_KEY_PRESSES #if defined(HAVE_X11_EXTENSIONS_XTEST_H) && defined(HAVE_X11_EXTENSIONS_XKB_H) #include #define CAN_SIMULATE_KEY_PRESSES #else /* HAVE_X11_EXTENSIONS_XTEST_H && HAVE_X11_EXTENSIONS_XKB_H */ #warning key press simulation not supported by this build - check that libxtst has been installed #endif /* HAVE_X11_EXTENSIONS_XTEST_H && HAVE_X11_EXTENSIONS_XKB_H */ #define BRLAPI_NO_DEPRECATED #include "brlapi.h" #include "options.h" //#define DEBUG #ifdef DEBUG #define debugf(fmt, ...) fprintf(stderr, fmt, ## __VA_ARGS__) #else /* DEBUG */ #define debugf(fmt, ...) (void)0 #endif /* DEBUG */ /****************************************************************************** * option handling */ static char *auth; static char *host; static char *xDisplay; static int quiet; static int brlapi_fd; BEGIN_OPTION_TABLE(programOptions) { .letter = 'b', .word = "brlapi", .argument = strtext("[host][:port]"), .setting.string = &host, .description = strtext("BrlAPI host and/or port to connect to") }, { .letter = 'a', .word = "auth", .argument = strtext("file"), .setting.string = &auth, .description = strtext("BrlAPI authorization/authentication string") }, { .letter = 'd', .word = "display", .argument = strtext("display"), .setting.string = &xDisplay, .description = strtext("X display to connect to") }, { .letter = 'q', .word = "quiet", .setting.flag = &quiet, .description = strtext("Do not write any text to the braille device") }, END_OPTION_TABLE /****************************************************************************** * error handling */ static void api_cleanExit(void) { if (brlapi_fd>=0) { close(brlapi_fd); brlapi_fd=-1; } } /* dumps errors which are fatal to brlapi only */ static void fatal_brlapi_errno(const char *msg, const char *fmt, ...) { brlapi_perror(msg); if (fmt) { va_list va; va_start(va,fmt); vfprintf(stderr,fmt,va); va_end(va); } api_cleanExit(); } static void exception_handler(int error, brlapi_packetType_t type, const void *packet, size_t size) { char str[0X100]; brlapi_strexception(str,0X100, error, type, packet, size); fprintf(stderr, "xbrlapi: BrlAPI exception: %s\nDisconnecting from brlapi\n", str); api_cleanExit(); } /* dumps errors which are fatal to the whole xbrlapi */ static void fatal_errno(const char *msg, const char *fmt, ...) { perror(msg); if (fmt) { va_list va; va_start(va,fmt); vfprintf(stderr,fmt,va); va_end(va); } exit(PROG_EXIT_FATAL); } static void fatal(const char *fmt, ...) { if (fmt) { va_list va; va_start(va,fmt); vfprintf(stderr,fmt,va); va_end(va); } exit(PROG_EXIT_FATAL); } /****************************************************************************** * brlapi handling */ #ifndef MIN #define MIN(a, b) (((a) < (b))? (a): (b)) #endif /* MIN */ static int tobrltty_init(char *auth, char *host) { brlapi_connectionSettings_t settings; unsigned int x,y; settings.host=host; settings.auth=auth; static int had_succeeded; if ((brlapi_fd = brlapi_openConnection(&settings,&settings))<0) { if (!had_succeeded) { /* This is the first attempt to connect to BRLTTY, and it failed. * Return the error immediately to the user, to provide feedback to users * running xbrlapi by hand, but not fill logs, eat battery, spam * 127.0.0.1 with reconnection attempts. */ fatal_brlapi_errno("openConnection",gettext("cannot connect to braille devices daemon brltty at %s\n"),settings.host); exit(PROG_EXIT_FATAL); } return 0; } /* We achieved connecting to BRLTTY. If BRLTTY dies later on, we will * silently try to reconnect to it. */ had_succeeded = 1; if (brlapi_getDisplaySize(&x,&y)<0) { fatal_brlapi_errno("getDisplaySize",NULL); return 0; } if (x == 0) { /* Braille device not initialized yet */ api_cleanExit(); return 0; } brlapi_setExceptionHandler(exception_handler); return 1; } static int getXVTnb(void); static void getVT(void) { char *path = getenv("WINDOWPATH"); char *vtnr = getenv("XDG_VTNR"); int vtno = -1; if (!path && !vtnr) /* Workaround for old xinit/xdm/gdm/kdm */ vtno = getXVTnb(); if (path || vtnr || vtno == -1) { if (brlapi_enterTtyModeWithPath(NULL,0,NULL)<0) { fatal_brlapi_errno("geTtyPath",gettext("cannot get tty\n")); return; } } else { if (brlapi_enterTtyMode(vtno,NULL)<0) { fatal_brlapi_errno("enterTtyMode",gettext("cannot get tty %d\n"),vtno); return; } } if (brlapi_ignoreAllKeys()<0) { fatal_brlapi_errno("ignoreAllKeys",gettext("cannot ignore keys\n")); return; } #ifdef CAN_SIMULATE_KEY_PRESSES /* All X keysyms with any modifier */ brlapi_keyCode_t cmd = BRLAPI_KEY_TYPE_SYM; if (brlapi_acceptKeys(brlapi_rangeType_type, &cmd, 1)) { fatal_brlapi_errno("acceptKeys",NULL); return; } cmd = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_SHIFT; if (brlapi_acceptKeys(brlapi_rangeType_key, &cmd, 1)) { fatal_brlapi_errno("acceptKeys",NULL); return; } cmd = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_UPPER; if (brlapi_acceptKeys(brlapi_rangeType_key, &cmd, 1)) { fatal_brlapi_errno("acceptKeys",NULL); return; } cmd = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_CONTROL; if (brlapi_acceptKeys(brlapi_rangeType_key, &cmd, 1)) { fatal_brlapi_errno("acceptKeys",NULL); return; } cmd = BRLAPI_KEY_TYPE_CMD | BRLAPI_KEY_CMD_META; if (brlapi_acceptKeys(brlapi_rangeType_key, &cmd, 1)) { fatal_brlapi_errno("acceptKeys",NULL); return; } #endif /* CAN_SIMULATE_KEY_PRESSES */ } static char *last_name; static void api_setLastName(void) { if (!last_name) return; if (brlapi_writeText(0,last_name)<0) { brlapi_perror("writeText"); fprintf(stderr,gettext("xbrlapi: cannot write window name %s\n"),last_name); } } static void api_setName(const char *wm_name) { if (brlapi_fd<0) return; debugf("%s got focus\n",wm_name); if (last_name) { if (!strcmp(wm_name,last_name)) return; free(last_name); } if (!(last_name=strdup(wm_name))) fatal_errno("strdup(wm_name)",NULL); api_setLastName(); } static int last_win; static void api_setLastFocus(void) { if (brlapi_setFocus(last_win)<0) fatal_brlapi_errno("setFocus",gettext("cannot set focus to %#010x\n"),last_win); } static void api_setFocus(int win) { if (brlapi_fd<0) return; debugf("%#010x (%d) got focus\n",win,win); last_win = win; api_setLastFocus(); } /****************************************************************************** * X handling */ static const char *Xdisplay; static Display *dpy; static Window curWindow; static Atom netWmNameAtom, utf8StringAtom; static volatile int grabFailed; #ifdef HAVE_ICONV_H iconv_t utf8Conv = (iconv_t)(-1); #endif /* HAVE_ICONV_H */ #define WINHASHBITS 12 static struct window { Window win; Window root; char *wm_name; struct window *next; } *windows[(1<>(32-WINHASHBITS)^(win&((1<win=win; cur->wm_name=wm_name; cur->root=root; cur->next=WINHASH(win); WINHASH(win)=cur; } static struct window *window_of_Window(Window win) { struct window *cur; for (cur=WINHASH(win); cur && cur->win!=win; cur=cur->next); return cur; } static int del_window(Window win) { struct window **pred; struct window *cur; for (pred=&WINHASH(win); cur = *pred, cur && cur->win!=win; pred=&cur->next); if (cur) { *pred=cur->next; free(cur->wm_name); free(cur); return 0; } else return -1; } static int ErrorHandler(Display *dpy, XErrorEvent *ev) { char buffer[128]; if (ev->error_code==BadWindow) { grabFailed=1; return 0; } if (!XGetErrorText(dpy, ev->error_code, buffer, sizeof(buffer))) fatal("XGetErrorText"); fprintf(stderr,gettext("xbrlapi: X Error %d, %s on display %s\n"), ev->type, buffer, XDisplayName(Xdisplay)); fprintf(stderr,gettext("xbrlapi: resource %#010lx, req %u:%u\n"),ev->resourceid,ev->request_code,ev->minor_code); exit(PROG_EXIT_FATAL); } static int getXVTnb(void) { Window root; Atom property; Atom actual_type; int actual_format; unsigned long nitems; unsigned long bytes_after; unsigned char *buf; int vt = -1; root=DefaultRootWindow(dpy); if ((property=XInternAtom(dpy,"XFree86_VT",False))==None) { fprintf(stderr,gettext("xbrlapi: no XFree86_VT atom\n")); return -1; } if (XGetWindowProperty(dpy,root,property,0,1,False,AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &buf)) { fprintf(stderr,gettext("xbrlapi: cannot get root window XFree86_VT property\n")); return -1; } if (nitems<1) { fprintf(stderr, gettext("xbrlapi: no items for VT number\n")); goto out; } if (nitems>1) fprintf(stderr,gettext("xbrlapi: more than one item for VT number\n")); switch (actual_type) { case XA_CARDINAL: case XA_INTEGER: case XA_WINDOW: switch (actual_format) { case 8: vt = (*(uint8_t *)buf); break; case 16: vt = (*(uint16_t *)buf); break; case 32: vt = (*(uint32_t *)buf); break; default: fprintf(stderr, gettext("xbrlapi: bad format for VT number\n")); goto out; } break; default: fprintf(stderr, gettext("xbrlapi: bad type for VT number\n")); goto out; } out: if (!XFree(buf)) fatal("XFree(VTnobuf)"); return vt; } static int grabWindow(Window win,int level) { #ifdef DEBUG char spaces[level+1]; #endif /* DEBUG */ grabFailed=0; if (!XSelectInput(dpy,win,PropertyChangeMask|FocusChangeMask|SubstructureNotifyMask) || grabFailed) return 0; #ifdef DEBUG memset(spaces,' ',level); spaces[level]='\0'; debugf("%sgrabbed %#010lx\n",spaces,win); #endif /* DEBUG */ return 1; } static char *getWindowTitle(Window win) { int wm_name_size=32; Atom actual_type; int actual_format; unsigned long nitems,bytes_after; unsigned char *wm_name=NULL; char *ret; do { if (XGetWindowProperty(dpy,win,netWmNameAtom,0,wm_name_size,False, /*XA_STRING*/AnyPropertyType,&actual_type,&actual_format,&nitems,&bytes_after, &wm_name)) { wm_name = NULL; break; /* window disappeared or not available */ } wm_name_size+=bytes_after; if (!bytes_after) break; if (!XFree(wm_name)) fatal("tempo_XFree(wm_name)"); } while (1); if (!wm_name) do { if (XGetWindowProperty(dpy,win,XA_WM_NAME,0,wm_name_size,False, /*XA_STRING*/AnyPropertyType,&actual_type,&actual_format,&nitems,&bytes_after, &wm_name)) return NULL; /* window disappeared */ if (wm_name_size >= nitems + 1) break; wm_name_size += bytes_after + 1; if (!XFree(wm_name)) fatal("tempo_XFree(wm_name)"); } while (1); if (actual_type==None) { XFree(wm_name); return NULL; } wm_name[nitems++] = 0; ret = strdup((char *) wm_name); XFree(wm_name); debugf("type %lx name %s len %ld\n",actual_type,ret,nitems); #ifdef HAVE_ICONV_H { if (actual_type == utf8StringAtom && utf8Conv != (iconv_t)(-1)) { char *ret2; size_t input_size, output_size; char *input, *output; input_size = nitems; input = ret; output_size = nitems * MB_CUR_MAX; output = ret2 = malloc(output_size); if (iconv(utf8Conv, &input, &input_size, &output, &output_size) == -1) { free(ret2); } else { free(ret); ret = realloc(ret2, nitems * MB_CUR_MAX - output_size); debugf("-> %s\n",ret); } } } #endif /* HAVE_ICONV_H */ return ret; } static int grabWindows(Window win,int level) { Window root,parent,*children; unsigned int nchildren,i; int res=1; if (!grabWindow(win,level)) return 1; /* window disappeared */ if (!XQueryTree(dpy,win,&root,&parent,&children,&nchildren)) return 0; add_window(win,root,getWindowTitle(win)); if (!children) return 1; for (i=0;iwm_name) if (window->win==window->root) api_setName("root"); else api_setName("unknown"); else api_setName(window->wm_name); } static void setFocus(Window win) { struct window *window; curWindow=win; api_setFocus((uint32_t)win); if (!quiet) { if (!(window=window_of_Window(win))) { fprintf(stderr,gettext("xbrlapi: didn't grab window %#010lx but got focus\n"),win); api_setName("unknown"); } else setName(window); } } #ifdef CAN_SIMULATE_KEY_PRESSES static int tryModifiers(KeyCode keycode, unsigned int *modifiers, unsigned int modifiers_try, KeySym keysym) { KeySym keysymRet; unsigned int modifiersRet; if (!XkbLookupKeySym(dpy, keycode, modifiers_try, &modifiersRet, &keysymRet)) return 0; if (keysymRet != keysym) return 0; *modifiers |= modifiers_try; return 1; } static void ignoreServerKeys(void) { brlapi_range_t range = { .first = BRLAPI_KEY_FLG(ControlMask|Mod1Mask), .last = BRLAPI_KEY_FLG(ControlMask|Mod1Mask)|~BRLAPI_KEY_FLAGS_MASK, }; if (brlapi_ignoreKeyRanges(&range, 1)) { fatal_brlapi_errno("ignoreKeyRanges",NULL); return; } } #endif /* CAN_SIMULATE_KEY_PRESSES */ static void toX_f(const char *display) { Window root; XEvent ev; int i; int X_fd; fd_set readfds; int maxfd; #ifdef CAN_SIMULATE_KEY_PRESSES int res; brlapi_keyCode_t code; unsigned int keysym, keycode, modifiers, next_modifiers = 0; Bool haveXTest; int eventBase, errorBase, majorVersion, minorVersion; XkbDescPtr xkb = NULL; XkbMapChangesRec changes = { .changed = XkbKeyTypesMask|XkbKeySymsMask }; int oneGroupType[XkbNumKbdGroups] = { XkbOneLevelIndex }; Status status; int last_remap_keycode = -1, remap_keycode; #endif /* CAN_SIMULATE_KEY_PRESSES */ Xdisplay = display; if (!Xdisplay) Xdisplay=getenv("DISPLAY"); if (!(dpy=XOpenDisplay(Xdisplay))) fatal(gettext("cannot connect to display %s\n"),Xdisplay); if (!XSetErrorHandler(ErrorHandler)) fatal(gettext("strange old error handler\n")); #ifdef CAN_SIMULATE_KEY_PRESSES haveXTest = XTestQueryExtension(dpy, &eventBase, &errorBase, &majorVersion, &minorVersion); { int foo; int major = XkbMajorVersion, minor = XkbMinorVersion; if (!XkbLibraryVersion(&major, &minor)) fatal(gettext("Incompatible XKB library\n")); if (!XkbQueryExtension(dpy, &foo, &foo, &foo, &major, &minor)) fatal(gettext("Incompatible XKB server support\n")); } #endif /* CAN_SIMULATE_KEY_PRESSES */ X_fd = XConnectionNumber(dpy); if (brlapi_fd>=0) { getVT(); #ifdef CAN_SIMULATE_KEY_PRESSES ignoreServerKeys(); #endif /* CAN_SIMULATE_KEY_PRESSES */ } netWmNameAtom = XInternAtom(dpy,"_NET_WM_NAME",False); utf8StringAtom = XInternAtom(dpy,"UTF8_STRING",False); #if defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV_H) { char *localCharset = nl_langinfo(CODESET); if (strcmp(localCharset, "UTF-8")) { char buf[strlen(localCharset) + 10 + 1]; snprintf(buf, sizeof(buf), "%s//TRANSLIT", localCharset); if ((utf8Conv = iconv_open(buf, "UTF-8")) == (iconv_t)(-1)) utf8Conv = iconv_open(localCharset, "UTF-8"); } } #endif /* defined(HAVE_NL_LANGINFO) && defined(HAVE_ICONV_H) */ for (i=0;i=0) FD_SET(brlapi_fd, &readfds); FD_SET(X_fd, &readfds); maxfd = brlapi_fd>=0 && X_fdroot,getWindowTitle(win)); } break; case DestroyNotify: debugf("win %#010lx destroyed\n",ev.xdestroywindow.window); if (del_window(ev.xdestroywindow.window)) debugf("destroy: didn't grab window %#010lx\n",ev.xdestroywindow.window); break; /* Property change: WM_NAME ? */ case PropertyNotify: if (ev.xproperty.atom==XA_WM_NAME || (netWmNameAtom != None && ev.xproperty.atom == netWmNameAtom)) { Window win = ev.xproperty.window; debugf("WM_NAME property of %#010lx changed\n",win); struct window *window; if (!(window=window_of_Window(win))) { fprintf(stderr,gettext("xbrlapi: didn't grab window %#010lx\n"),win); add_window(win,None,getWindowTitle(win)); } else { if (window->wm_name) if (!XFree(window->wm_name)) fatal(gettext("XFree(wm_name) for change")); if ((window->wm_name=getWindowTitle(win))) { if (!quiet && win==curWindow) api_setName(window->wm_name); } else fprintf(stderr,gettext("xbrlapi: window %#010lx changed to NULL name\n"),win); } } break; case MappingNotify: XRefreshKeyboardMapping(&ev.xmapping); break; /* ignored events */ case UnmapNotify: case MapNotify: case MapRequest: case ReparentNotify: case ConfigureNotify: case GravityNotify: case ConfigureRequest: case CirculateNotify: case CirculateRequest: case ClientMessage: break; /* "shouldn't happen" events */ default: fprintf(stderr,gettext("xbrlapi: unhandled event type: %d\n"),ev.type); break; } } if (brlapi_fd>=0) { #ifdef CAN_SIMULATE_KEY_PRESSES if (haveXTest && FD_ISSET(brlapi_fd,&readfds)) { while (((res = brlapi_readKey(0, &code))==1)) { switch (code & BRLAPI_KEY_TYPE_MASK) { case BRLAPI_KEY_TYPE_CMD: switch (code & BRLAPI_KEY_CODE_MASK) { { unsigned int modifier; case BRLAPI_KEY_CMD_SHIFT: modifier = ShiftMask; goto doModifier; case BRLAPI_KEY_CMD_UPPER: modifier = LockMask; goto doModifier; case BRLAPI_KEY_CMD_CONTROL: modifier = ControlMask; goto doModifier; case BRLAPI_KEY_CMD_META: modifier = Mod1Mask; goto doModifier; doModifier: switch (code & BRLAPI_KEY_FLG_TOGGLE_MASK) { case 0: next_modifiers ^= modifier; break; case BRLAPI_KEY_FLG_TOGGLE_ON: next_modifiers |= modifier; break; case BRLAPI_KEY_FLG_TOGGLE_OFF: next_modifiers &= ~modifier; break; default: case BRLAPI_KEY_FLG_TOGGLE_ON | BRLAPI_KEY_FLG_TOGGLE_OFF: break; } break; } default: fprintf(stderr, "xbrlapi: %s: %" BRLAPI_PRIxKEYCODE "\n", gettext("unexpected cmd"), code); break; } break; case BRLAPI_KEY_TYPE_SYM: modifiers = ((code & BRLAPI_KEY_FLAGS_MASK) >> BRLAPI_KEY_FLAGS_SHIFT) & 0xFF; keysym = code & BRLAPI_KEY_CODE_MASK; keycode = XKeysymToKeycode(dpy,keysym); remap_keycode = -1; if (keycode == NoSymbol) { debugf(gettext("xbrlapi: Couldn't translate keysym %08X to keycode.\n"),keysym); goto needRemap; } { static const unsigned int tryTable[] = { 0, ShiftMask, Mod2Mask, Mod3Mask, Mod4Mask, Mod5Mask, ShiftMask|Mod2Mask, ShiftMask|Mod3Mask, ShiftMask|Mod4Mask, ShiftMask|Mod5Mask, 0 }; const unsigned int *try = tryTable; do { if (tryModifiers(keycode, &modifiers, *try, keysym)) goto foundModifiers; } while (*++try); debugf(gettext("xbrlapi: Couldn't find modifiers to apply to %d for getting keysym %08X\n"),keycode,keysym); } needRemap: { /* Try tofind an unassigned keycode to remap it temporarily to the requested keysym. */ xkb = XkbGetMap(dpy,XkbKeyTypesMask|XkbKeySymsMask,XkbUseCoreKbd); /* Start from big keycodes, usually unassigned. */ for (i = xkb->max_key_code; i >= xkb->min_key_code && (XkbKeyNumGroups(xkb,i) != 0 || i == last_remap_keycode); i--) ; if (i < xkb->min_key_code) { fprintf(stderr,gettext("xbrlapi: Couldn't find a keycode to remap for simulating unbound keysym %08X\n"),keysym); goto abortRemap; } remap_keycode = keycode = i; next_modifiers = modifiers = 0; /* Remap this keycode. */ changes.first_key_sym = keycode; changes.num_key_syms = 1; if ((status = XkbChangeTypesOfKey(xkb,keycode,1,XkbGroup1Mask,oneGroupType,&changes))) { debugf("Error while changing client keymap: %d\n", status); goto abortRemap; } XkbKeySymEntry(xkb,keycode,0,0) = keysym; if (!XkbChangeMap(dpy,xkb,&changes)) { debugf("Error while changing server keymap\n"); goto abortRemap; } XkbFreeKeyboard(xkb,0,True); debugf("Remapped keycode %d to keysym %08X\n",keycode,keysym); } foundModifiers: debugf("key %08X: (%d,%x,%x)\n", keysym, keycode, next_modifiers, modifiers); modifiers |= next_modifiers; next_modifiers = 0; if (modifiers) XkbLockModifiers(dpy, XkbUseCoreKbd, modifiers, modifiers); XTestFakeKeyEvent(dpy,keycode,True,1); XTestFakeKeyEvent(dpy,keycode,False,1); if (modifiers) XkbLockModifiers(dpy, XkbUseCoreKbd, modifiers, 0); /* Remove previous keycode mapping */ if (last_remap_keycode != -1) { /* Note: since X11 is asynchronous, we should not immediately * unmap the just-mapped keycode, otherwise when the client * eventually gets to read the new Xkb state from the server, * the key might have been synthesized and the keycode unmapped * already. We just hope the user does not type too fast for the * application to catch up. */ xkb = XkbGetMap(dpy,XkbKeyTypesMask|XkbKeySymsMask,XkbUseCoreKbd); changes.first_key_sym = last_remap_keycode; changes.num_key_syms = 1; if ((status = XkbChangeTypesOfKey(xkb,last_remap_keycode,0,XkbGroup1Mask,NULL,&changes))) { debugf("Oops, error while restoring client keymap: %d\n", status); } else { XkbChangeMap(dpy,xkb,&changes); debugf("restored last keycode %d\n", last_remap_keycode); } XkbFreeKeyboard(xkb,0,True); } XFlush(dpy); last_remap_keycode = remap_keycode; break; abortRemap: XkbFreeKeyboard(xkb, 0, True); xkb = NULL; break; default: fprintf(stderr, "xbrlapi: %s: %" BRLAPI_PRIxKEYCODE "\n", gettext("unexpected block type"), code); next_modifiers = 0; break; } } if (res<0) fatal_brlapi_errno("brlapi_readKey",NULL); } #endif /* CAN_SIMULATE_KEY_PRESSES */ } else { /* Try to reconnect */ if (tobrltty_init(auth,host)) { getVT(); #ifdef CAN_SIMULATE_KEY_PRESSES ignoreServerKeys(); #endif /* CAN_SIMULATE_KEY_PRESSES */ api_setLastName(); api_setLastFocus(); } } } } /****************************************************************************** * main */ static void term_handler(int foo) { api_cleanExit(); exit(PROG_EXIT_SUCCESS); } int main (int argc, char *argv[]) { { static const OptionsDescriptor descriptor = { OPTION_TABLE(programOptions), .applicationName = "xbrlapi" }; PROCESS_OPTIONS(descriptor, argc, argv); } signal(SIGTERM,term_handler); signal(SIGINT,term_handler); #ifdef SIGHUP signal(SIGHUP,term_handler); #endif /* SIGHUP */ #ifdef SIGQUIT signal(SIGQUIT,term_handler); #endif /* SIGQUIT */ #ifdef SIGPIPE signal(SIGPIPE,term_handler); #endif /* SIGPIPE */ tobrltty_init(auth,host); toX_f(xDisplay); return PROG_EXIT_SUCCESS; }