| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY 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 <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <limits.h> |
| #include <locale.h> |
| |
| #ifdef __MINGW32__ |
| #include "win_pthread.h" |
| #else /* __MINGW32__ */ |
| #include <semaphore.h> |
| #endif /* __MINGW32__ */ |
| |
| #include <cspi/spi.h> |
| #include <X11/keysym.h> |
| #include <X11/Xlib.h> |
| |
| #include "log.h" |
| #include "parse.h" |
| #include "thread.h" |
| #include "brl_cmds.h" |
| |
| typedef enum { |
| PARM_TYPE |
| } ScreenParameters; |
| #define SCRPARMS "type" |
| |
| #include "scr_driver.h" |
| |
| static int typeText = 0, typeTerminal = 1, typeAll = 0; |
| static AccessibleText *curTerm; |
| static Accessible *curFocus; |
| |
| static long curNumRows, curNumCols; |
| static wchar_t **curRows; |
| static long *curRowLengths; |
| static long curPosX,curPosY; |
| static pthread_mutex_t updateMutex = PTHREAD_MUTEX_INITIALIZER; |
| |
| pthread_t SPI_main_thread; |
| |
| /* having our own implementation is much more independant on locales */ |
| |
| typedef struct { |
| int remaining; |
| wint_t current; |
| } my_mbstate_t; |
| |
| static my_mbstate_t internal; |
| |
| static size_t my_mbrtowc(wchar_t *pwc, const char *s, size_t n, my_mbstate_t *ps) { |
| const unsigned char *c = (const unsigned char *) s; |
| int read = 0; |
| if (!c) { |
| if (ps->remaining) { |
| errno = EILSEQ; |
| return (size_t)(-1); |
| } |
| return 0; |
| } |
| |
| if (n && !ps->remaining) { |
| /* initial state */ |
| if (!(*c&0x80)) { |
| /* most frequent case: ascii */ |
| if (pwc) |
| *pwc = *c; |
| if (!*c) |
| return 0; |
| return 1; |
| } else if (!(*c&0x40)) { |
| /* utf-8 char continuation, shouldn't happen with remaining == 0 ! */ |
| goto error; |
| } else { |
| /* new utf-8 char, get remaining chars */ |
| read = 1; |
| if (!(*c&0x20)) { |
| ps->remaining = 1; |
| ps->current = *c&((1<<5)-1); |
| } else if (!(*c&0x10)) { |
| ps->remaining = 2; |
| ps->current = *c&((1<<4)-1); |
| } else if (!(*c&0x08)) { |
| ps->remaining = 3; |
| ps->current = *c&((1<<3)-1); |
| } else if (!(*c&0x04)) { |
| ps->remaining = 4; |
| ps->current = *c&((1<<2)-1); |
| } else if (!(*c&0x02)) { |
| ps->remaining = 5; |
| ps->current = *c&((1<<1)-1); |
| } else |
| /* 0xff and 0xfe are not allowed */ |
| goto error; |
| c++; |
| } |
| } |
| /* looking for continuation chars */ |
| while (n-read) { |
| if ((*c&0xc0) != 0X80) |
| /* not continuation char, error ! */ |
| goto error; |
| /* utf-8 char continuation */ |
| ps->current = (ps->current<<6) | (*c&((1<<6)-1)); |
| read++; |
| if (!(--ps->remaining)) { |
| if (pwc) |
| *pwc = ps->current; |
| if (!ps->current) |
| /* shouldn't coded this way, but well... */ |
| return 0; |
| return read; |
| } |
| c++; |
| } |
| return (size_t)(-2); |
| error: |
| errno = EILSEQ; |
| return (size_t)(-1); |
| } |
| |
| static size_t my_mbsrtowcs(wchar_t *dest, const char **src, size_t len, my_mbstate_t *ps) { |
| int put = 0; |
| size_t skip; |
| wchar_t buf,*bufp; |
| |
| if (!ps) ps = &internal; |
| if (dest) |
| bufp = dest; |
| else |
| bufp = &buf; |
| |
| while (len-put || !dest) { |
| skip = my_mbrtowc(bufp, *src, 6, ps); |
| switch (skip) { |
| case (size_t)(-2): errno = EILSEQ; /* shouldn't happen ! */ |
| case (size_t)(-1): return (size_t)(-1); |
| case 0: *src = NULL; return put; |
| } |
| *src += skip; |
| if (dest) bufp++; |
| put++; |
| } |
| return put; |
| } |
| |
| static size_t my_mbrlen(const char *s, size_t n, my_mbstate_t *ps) { |
| return my_mbrtowc(NULL, s, n, ps?ps:&internal); |
| } |
| |
| static size_t my_mbslen(const char *s, size_t n) { |
| my_mbstate_t ps; |
| size_t ret=0; |
| size_t eaten; |
| memset(&ps,0,sizeof(ps)); |
| while(n) { |
| if ((eaten = my_mbrlen(s,n,&ps))<0) |
| return eaten; |
| if (!(eaten)) return ret; |
| s+=eaten; |
| n-=eaten; |
| ret++; |
| } |
| return ret; |
| } |
| |
| static void addRows(long pos, long num) { |
| curNumRows += num; |
| curRows = realloc(curRows,curNumRows*sizeof(*curRows)); |
| curRowLengths = realloc(curRowLengths,curNumRows*sizeof(*curRowLengths)); |
| memmove(curRows +pos+num,curRows +pos,(curNumRows-(pos+num))*sizeof(*curRows)); |
| memmove(curRowLengths+pos+num,curRowLengths+pos,(curNumRows-(pos+num))*sizeof(*curRowLengths)); |
| } |
| |
| static void delRows(long pos, long num) { |
| long y; |
| for (y=pos;y<pos+num;y++) |
| free(curRows[y]); |
| memmove(curRows +pos,curRows +pos+num,(curNumRows-(pos+num))*sizeof(*curRows)); |
| memmove(curRowLengths+pos,curRowLengths+pos+num,(curNumRows-(pos+num))*sizeof(*curRowLengths)); |
| curNumRows -= num; |
| curRows = realloc(curRows,curNumRows*sizeof(*curRows)); |
| curRowLengths = realloc(curRowLengths,curNumRows*sizeof(*curRowLengths)); |
| } |
| |
| static int |
| processParameters_AtSpiScreen (char **parameters) { |
| if (*parameters[PARM_TYPE]) { |
| static const char *const choices[] = {"text" , "terminal" , "all" , NULL}; |
| static int *const flags [] = {&typeText, &typeTerminal, &typeAll, NULL}; |
| int count; |
| char **types = splitString(parameters[PARM_TYPE], '+', &count); |
| |
| { |
| int *const *flag = flags; |
| while (*flag) **flag++ = 0; |
| } |
| |
| { |
| int index; |
| for (index=0; index<count; index++) { |
| const char *type = types[index]; |
| unsigned int choice; |
| |
| if (validateChoice(&choice, type, choices)) { |
| int *flag = flags[choice]; |
| if ((flag == &typeAll) && (index > 0)) { |
| logMessage(LOG_WARNING, "widget type is mutually exclusive: %s", type); |
| } else if (*flag || typeAll) { |
| logMessage(LOG_WARNING, "widget type specified more than once: %s", type); |
| } else { |
| *flag = 1; |
| } |
| } else { |
| logMessage(LOG_WARNING, "%s: %s", "invalid widget type", type); |
| } |
| } |
| } |
| |
| deallocateStrings(types); |
| } |
| |
| return 1; |
| } |
| |
| static void findPosition(long position, long *px, long *py) { |
| long offset=0, newoffset, x, y; |
| /* XXX: I don't know what they do with necessary combining accents */ |
| for (y=0; y<curNumRows; y++) { |
| if ((newoffset = offset + curRowLengths[y]) > position) |
| break; |
| offset = newoffset; |
| } |
| if (y==curNumRows) { |
| if (!curNumRows) { |
| y = 0; |
| x = 0; |
| } else { |
| /* this _can_ happen, when deleting while caret is at the end of the |
| * terminal: caret position is only updated afterwards... In the |
| * meanwhile, keep caret at the end of last line. */ |
| y = curNumRows-1; |
| x = curRowLengths[y]; |
| } |
| } else |
| x = position-offset; |
| *px = x; |
| *py = y; |
| } |
| |
| static void caretPosition(long caret) { |
| findPosition(caret,&curPosX,&curPosY); |
| } |
| |
| static void finiTerm(void) { |
| logMessage(LOG_DEBUG,"end of term %p",curTerm); |
| Accessible_unref(curTerm); |
| curTerm = NULL; |
| Accessible_unref(curFocus); |
| curFocus = NULL; |
| curPosX = curPosY = 0; |
| } |
| |
| static void restartTerm(Accessible *newTerm, AccessibleText *newTextTerm) { |
| char *c,*d; |
| const char *e; |
| long i,len; |
| char *text; |
| |
| if (curFocus) |
| finiTerm(); |
| Accessible_ref(curFocus = newTerm); |
| curTerm = newTextTerm; |
| logMessage(LOG_DEBUG,"new term %p",curTerm); |
| text = AccessibleText_getText(curTerm,0,LONG_MAX); |
| curNumRows = 0; |
| if (curRows) { |
| for (i=0;i<curNumRows;i++) |
| free(curRows[i]); |
| free(curRows); |
| } |
| free(curRowLengths); |
| c = text; |
| while (*c) { |
| curNumRows++; |
| if (!(c = strchr(c,'\n'))) |
| break; |
| c++; |
| } |
| logMessage(LOG_DEBUG,"%ld rows",curNumRows); |
| curRows = malloc(curNumRows * sizeof(*curRows)); |
| curRowLengths = malloc(curNumRows * sizeof(*curRowLengths)); |
| i = 0; |
| curNumCols = 0; |
| for (c = text; *c; c = d+1) { |
| d = strchr(c,'\n'); |
| if (d) |
| *d = 0; |
| e = c; |
| curRowLengths[i] = (len = my_mbsrtowcs(NULL,&e,0,NULL)) + (d != NULL); |
| if (len > curNumCols) |
| curNumCols = len; |
| else if (len < 0) { |
| if (len==-2) |
| logMessage(LOG_ERR,"unterminated sequence %s",c); |
| else if (len==-1) |
| logSystemError("mbrlen"); |
| curRowLengths[i] = (len = -1) + (d != NULL); |
| } |
| curRows[i] = malloc((len + (d!=NULL)) * sizeof(*curRows[i])); |
| e = c; |
| my_mbsrtowcs(curRows[i],&e,len,NULL); |
| if (d) |
| curRows[i][len]='\n'; |
| else |
| break; |
| i++; |
| } |
| logMessage(LOG_DEBUG,"%ld cols",curNumCols); |
| SPI_freeString(text); |
| caretPosition(AccessibleText_getCaretOffset(curTerm)); |
| } |
| |
| static struct evList { |
| struct evList *next; |
| const AccessibleEvent *ev; |
| } *evs; |
| |
| static void evListenerCB(const AccessibleEvent *event, void *user_data) { |
| static int running = 0; |
| struct evList *ev = malloc(sizeof(*ev)); |
| AccessibleEvent_ref(event); |
| AccessibleText *newText; |
| ev->next = evs; |
| ev->ev = event; |
| evs = ev; |
| int state_changed_focused; |
| |
| /* this is not atomic but we can only be recursively called within calls |
| * to the lib */ |
| if (running) |
| return; |
| else |
| running = 1; |
| |
| while (evs) { |
| pthread_mutex_lock(&updateMutex); |
| /* pickup a list of events to handle */ |
| ev = evs; |
| evs = NULL; |
| for (; ev; AccessibleEvent_unref(ev->ev), ev = ev->next) { |
| event = ev->ev; |
| state_changed_focused = !strcmp(event->type,"object:state-changed:focused"); |
| if (state_changed_focused && !event->detail1) { |
| if (event->source == curFocus) |
| finiTerm(); |
| } else if (!strcmp(event->type,"focus:") || (state_changed_focused && event->detail1)) { |
| if (!(newText = Accessible_getText(event->source))) { |
| if (curFocus) finiTerm(); |
| } else { |
| AccessibleRole role = Accessible_getRole(event->source); |
| if (typeAll || |
| (typeText && ((role == SPI_ROLE_TEXT) || (role == SPI_ROLE_PASSWORD_TEXT) || (role == SPI_ROLE_PARAGRAPH))) || |
| (typeTerminal && (role == SPI_ROLE_TERMINAL))) { |
| restartTerm(event->source, newText); |
| } else { |
| logMessage(LOG_DEBUG,"AT SPI widget not for us"); |
| if (curFocus) finiTerm(); |
| } |
| } |
| } else if (!strcmp(event->type,"object:text-caret-moved")) { |
| if (event->source != curFocus) continue; |
| logMessage(LOG_DEBUG,"caret move to %lu",event->detail1); |
| caretPosition(event->detail1); |
| } else if (!strcmp(event->type,"object:text-changed:delete")) { |
| long x,y,toDelete = event->detail2; |
| long length = 0, toCopy; |
| long downTo; /* line that will provide what will follow x */ |
| logMessage(LOG_DEBUG,"delete %lu from %lu",event->detail2,event->detail1); |
| if (event->source != curFocus) continue; |
| findPosition(event->detail1,&x,&y); |
| downTo = y; |
| if (downTo < curNumRows) |
| length = curRowLengths[downTo]; |
| while (x+toDelete >= length) { |
| downTo++; |
| if (downTo <= curNumRows - 1) |
| length += curRowLengths[downTo]; |
| else { |
| /* imaginary extra line doesn't provide more length, and shouldn't need to ! */ |
| if (x+toDelete > length) { |
| logMessage(LOG_ERR,"deleting past end of text !"); |
| /* discarding */ |
| toDelete = length - x; |
| } |
| break; /* deleting up to end */ |
| } |
| } |
| if (length-toDelete>0) { |
| /* still something on line y */ |
| if (y!=downTo) { |
| curRowLengths[y] = length-toDelete; |
| curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); |
| } |
| if ((toCopy = length-toDelete-x)) |
| memmove(curRows[y]+x,curRows[downTo]+curRowLengths[downTo]-toCopy,toCopy*sizeof(*curRows[downTo])); |
| if (y==downTo) { |
| curRowLengths[y] = length-toDelete; |
| curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); |
| } |
| } else { |
| /* kills this line as well ! */ |
| y--; |
| } |
| if (downTo>=curNumRows) |
| /* imaginary extra lines don't need to be deleted */ |
| downTo=curNumRows-1; |
| delRows(y+1,downTo-y); |
| caretPosition(AccessibleText_getCaretOffset(curTerm)); |
| } else if (!strcmp(event->type,"object:text-changed:insert")) { |
| long len=event->detail2,semilen,x,y; |
| char *added; |
| const char *adding,*c; |
| logMessage(LOG_DEBUG,"insert %lu from %lu",event->detail2,event->detail1); |
| if (event->source != curFocus) continue; |
| findPosition(event->detail1,&x,&y); |
| adding = c = added = AccessibleTextChangedEvent_getChangeString(event); |
| if (x && (c = strchr(adding,'\n'))) { |
| /* splitting line */ |
| addRows(y,1); |
| semilen=my_mbslen(adding,c+1-adding); |
| curRowLengths[y]=x+semilen; |
| if (x+semilen-1>curNumCols) |
| curNumCols=x+semilen-1; |
| |
| /* copy beginning */ |
| curRows[y]=malloc(curRowLengths[y]*sizeof(*curRows[y])); |
| memcpy(curRows[y],curRows[y+1],x*sizeof(*curRows[y])); |
| /* add */ |
| my_mbsrtowcs(curRows[y]+x,&adding,semilen,NULL); |
| len-=semilen; |
| adding=c+1; |
| /* shift end */ |
| curRowLengths[y+1]-=x; |
| memmove(curRows[y+1],curRows[y+1]+x,curRowLengths[y+1]*sizeof(*curRows[y+1])); |
| x=0; |
| y++; |
| } |
| while ((c = strchr(adding,'\n'))) { |
| /* adding lines */ |
| addRows(y,1); |
| semilen=my_mbslen(adding,c+1-adding); |
| curRowLengths[y]=semilen; |
| if (semilen-1>curNumCols) |
| curNumCols=semilen-1; |
| curRows[y]=malloc(semilen*sizeof(*curRows[y])); |
| my_mbsrtowcs(curRows[y],&adding,semilen,NULL); |
| len-=semilen; |
| adding=c+1; |
| y++; |
| } |
| if (len) { |
| /* still length to add on the line following it */ |
| if (y==curNumRows) { |
| /* It won't insert ending \n yet */ |
| addRows(y,1); |
| curRows[y]=NULL; |
| curRowLengths[y]=0; |
| } |
| curRowLengths[y] += len; |
| curRows[y]=realloc(curRows[y],curRowLengths[y]*sizeof(*curRows[y])); |
| memmove(curRows[y]+x+len,curRows[y]+x,(curRowLengths[y]-(x+len))*sizeof(*curRows[y])); |
| my_mbsrtowcs(curRows[y]+x,&adding,len,NULL); |
| if (curRowLengths[y]-(curRows[y][curRowLengths[y]-1]=='\n')>curNumCols) |
| curNumCols=curRowLengths[y]-(curRows[y][curRowLengths[y]-1]=='\n'); |
| } |
| SPI_freeString(added); |
| caretPosition(AccessibleText_getCaretOffset(curTerm)); |
| } else |
| logMessage(LOG_INFO,"event %s, source %p, detail1 %lu detail2 %lu",event->type,event->source,event->detail1,event->detail2); |
| } |
| pthread_mutex_unlock(&updateMutex); |
| } |
| running = 0; |
| } |
| |
| THREAD_FUNCTION(asOpenScreenThread) { |
| AccessibleEventListener *evListener; |
| sem_t *SPI_init_sem = argument; |
| int res; |
| static const char *events[] = { |
| "object:text-changed", |
| "object:text-caret-moved", |
| "object:state-changed:focused", |
| "focus:", |
| }; |
| const char **event; |
| if ((res=SPI_init())) { |
| logMessage(LOG_ERR,"SPI_init returned %d",res); |
| return 0; |
| } |
| if (!(evListener = SPI_createAccessibleEventListener(evListenerCB,NULL))) |
| logMessage(LOG_ERR,"SPI_createAccessibleEventListener failed"); |
| else for (event=events; event<&events[sizeof(events)/sizeof(*events)]; event++) |
| if (!(SPI_registerGlobalEventListener(evListener,*event))) |
| logMessage(LOG_ERR,"SPI_registerGlobalEventListener(%s) failed",*event); |
| sem_post(SPI_init_sem); |
| SPI_event_main(); |
| if (!(SPI_deregisterGlobalEventListenerAll(evListener))) |
| logMessage(LOG_ERR,"SPI_deregisterGlobalEventListenerAll failed"); |
| AccessibleEventListener_unref(evListener); |
| if (curFocus) |
| finiTerm(); |
| if ((res=SPI_exit())) |
| logMessage(LOG_ERR,"SPI_exit returned %d",res); |
| return NULL; |
| } |
| |
| static int |
| construct_AtSpiScreen (void) { |
| sem_t SPI_init_sem; |
| sem_init(&SPI_init_sem,0,0); |
| XInitThreads(); |
| if (createThread("driver-screen-AtSpi", |
| &SPI_main_thread, NULL, |
| asOpenScreenThread, (void *)&SPI_init_sem)) { |
| logMessage(LOG_ERR,"main SPI thread failed to be launched"); |
| return 0; |
| } |
| do { |
| errno = 0; |
| } while (sem_wait(&SPI_init_sem) == -1 && errno == EINTR); |
| if (errno) { |
| logSystemError("SPI initialization wait failed"); |
| return 0; |
| } |
| logMessage(LOG_DEBUG,"SPI initialized"); |
| return 1; |
| } |
| |
| static void |
| destruct_AtSpiScreen (void) { |
| SPI_event_quit(); |
| pthread_join(SPI_main_thread,NULL); |
| logMessage(LOG_DEBUG,"SPI stopped"); |
| } |
| |
| static int |
| currentVirtualTerminal_AtSpiScreen (void) { |
| return curTerm? 0: -1; |
| } |
| |
| static const char nonatspi [] = "not an AT-SPI text widget"; |
| |
| static void |
| describe_AtSpiScreen (ScreenDescription *description) { |
| pthread_mutex_lock(&updateMutex); |
| if (curTerm) { |
| description->cols = curNumCols; |
| description->rows = curNumRows?curNumRows:1; |
| description->posx = curPosX; |
| description->posy = curPosY; |
| } else { |
| description->unreadable = nonatspi; |
| description->rows = 1; |
| description->cols = strlen(nonatspi); |
| description->posx = 0; |
| description->posy = 0; |
| } |
| pthread_mutex_unlock(&updateMutex); |
| description->number = currentVirtualTerminal_AtSpiScreen(); |
| } |
| |
| static int |
| readCharacters_AtSpiScreen (const ScreenBox *box, ScreenCharacter *buffer) { |
| long x,y; |
| clearScreenCharacters(buffer,box->height*box->width); |
| pthread_mutex_lock(&updateMutex); |
| if (!curTerm) { |
| setScreenMessage(box, buffer, nonatspi); |
| goto out; |
| } |
| if (!curNumRows) { |
| goto out; |
| } |
| if (!validateScreenBox(box, curNumCols, curNumRows)) { |
| goto out; |
| } |
| for (y=0; y<box->height; y++) { |
| if (curRowLengths[box->top+y]) { |
| for (x=0; x<box->width; x++) { |
| if (box->left+x<curRowLengths[box->top+y] - (curRows[box->top+y][curRowLengths[box->top+y]-1]=='\n')) { |
| buffer[y*box->width+x].text = curRows[box->top+y][box->left+x]; |
| } |
| } |
| } |
| } |
| out: |
| pthread_mutex_unlock(&updateMutex); |
| return 1; |
| } |
| |
| static int |
| insertKey_AtSpiScreen (ScreenKey key) { |
| long keysym; |
| int modMeta=0, modControl=0; |
| |
| setScreenKeyModifiers(&key, SCR_KEY_CONTROL); |
| |
| if (isSpecialKey(key)) { |
| switch (key & SCR_KEY_CHAR_MASK) { |
| case SCR_KEY_ENTER: keysym = XK_KP_Enter; break; |
| case SCR_KEY_TAB: keysym = XK_Tab; break; |
| case SCR_KEY_BACKSPACE: keysym = XK_BackSpace; break; |
| case SCR_KEY_ESCAPE: keysym = XK_Escape; break; |
| case SCR_KEY_CURSOR_LEFT: keysym = XK_Left; break; |
| case SCR_KEY_CURSOR_RIGHT: keysym = XK_Right; break; |
| case SCR_KEY_CURSOR_UP: keysym = XK_Up; break; |
| case SCR_KEY_CURSOR_DOWN: keysym = XK_Down; break; |
| case SCR_KEY_PAGE_UP: keysym = XK_Page_Up; break; |
| case SCR_KEY_PAGE_DOWN: keysym = XK_Page_Down; break; |
| case SCR_KEY_HOME: keysym = XK_Home; break; |
| case SCR_KEY_END: keysym = XK_End; break; |
| case SCR_KEY_INSERT: keysym = XK_Insert; break; |
| case SCR_KEY_DELETE: keysym = XK_Delete; break; |
| case SCR_KEY_FUNCTION + 0: keysym = XK_F1; break; |
| case SCR_KEY_FUNCTION + 1: keysym = XK_F2; break; |
| case SCR_KEY_FUNCTION + 2: keysym = XK_F3; break; |
| case SCR_KEY_FUNCTION + 3: keysym = XK_F4; break; |
| case SCR_KEY_FUNCTION + 4: keysym = XK_F5; break; |
| case SCR_KEY_FUNCTION + 5: keysym = XK_F6; break; |
| case SCR_KEY_FUNCTION + 6: keysym = XK_F7; break; |
| case SCR_KEY_FUNCTION + 7: keysym = XK_F8; break; |
| case SCR_KEY_FUNCTION + 8: keysym = XK_F9; break; |
| case SCR_KEY_FUNCTION + 9: keysym = XK_F10; break; |
| case SCR_KEY_FUNCTION + 10: keysym = XK_F11; break; |
| case SCR_KEY_FUNCTION + 11: keysym = XK_F12; break; |
| case SCR_KEY_FUNCTION + 12: keysym = XK_F13; break; |
| case SCR_KEY_FUNCTION + 13: keysym = XK_F14; break; |
| case SCR_KEY_FUNCTION + 14: keysym = XK_F15; break; |
| case SCR_KEY_FUNCTION + 15: keysym = XK_F16; break; |
| case SCR_KEY_FUNCTION + 16: keysym = XK_F17; break; |
| case SCR_KEY_FUNCTION + 17: keysym = XK_F18; break; |
| case SCR_KEY_FUNCTION + 18: keysym = XK_F19; break; |
| case SCR_KEY_FUNCTION + 19: keysym = XK_F20; break; |
| case SCR_KEY_FUNCTION + 20: keysym = XK_F21; break; |
| case SCR_KEY_FUNCTION + 21: keysym = XK_F22; break; |
| case SCR_KEY_FUNCTION + 22: keysym = XK_F23; break; |
| case SCR_KEY_FUNCTION + 23: keysym = XK_F24; break; |
| case SCR_KEY_FUNCTION + 24: keysym = XK_F25; break; |
| case SCR_KEY_FUNCTION + 25: keysym = XK_F26; break; |
| case SCR_KEY_FUNCTION + 26: keysym = XK_F27; break; |
| case SCR_KEY_FUNCTION + 27: keysym = XK_F28; break; |
| case SCR_KEY_FUNCTION + 28: keysym = XK_F29; break; |
| case SCR_KEY_FUNCTION + 29: keysym = XK_F30; break; |
| case SCR_KEY_FUNCTION + 30: keysym = XK_F31; break; |
| case SCR_KEY_FUNCTION + 31: keysym = XK_F32; break; |
| case SCR_KEY_FUNCTION + 32: keysym = XK_F33; break; |
| case SCR_KEY_FUNCTION + 33: keysym = XK_F34; break; |
| case SCR_KEY_FUNCTION + 34: keysym = XK_F35; break; |
| default: logMessage(LOG_WARNING, "key not insertable: %04X", key); return 0; |
| } |
| } else { |
| wchar_t wc; |
| |
| if (key & SCR_KEY_ALT_LEFT) { |
| key &= ~SCR_KEY_ALT_LEFT; |
| modMeta = 1; |
| } |
| |
| if (key & SCR_KEY_CONTROL) { |
| key &= ~SCR_KEY_CONTROL; |
| modControl = 1; |
| } |
| |
| wc = key & SCR_KEY_CHAR_MASK; |
| if (wc < 0x100) |
| keysym = wc; /* latin1 character */ |
| else |
| keysym = 0x1000000 | wc; |
| } |
| logMessage(LOG_DEBUG, "inserting key: %04X -> %s%s%ld", |
| key, |
| (modMeta? "meta ": ""), |
| (modControl? "control ": ""), |
| keysym); |
| |
| { |
| int ok = 0; |
| |
| if (!modMeta || SPI_generateKeyboardEvent(XK_Meta_L,NULL,SPI_KEY_PRESS)) { |
| if (!modControl || SPI_generateKeyboardEvent(XK_Control_L,NULL,SPI_KEY_PRESS)) { |
| if (SPI_generateKeyboardEvent(keysym,NULL,SPI_KEY_SYM)) { |
| ok = 1; |
| } else { |
| logMessage(LOG_WARNING, "key insertion failed."); |
| } |
| |
| if (modControl && !SPI_generateKeyboardEvent(XK_Control_L,NULL,SPI_KEY_RELEASE)) { |
| logMessage(LOG_WARNING, "control release failed."); |
| ok = 0; |
| } |
| } else { |
| logMessage(LOG_WARNING, "control press failed."); |
| } |
| |
| if (modMeta && !SPI_generateKeyboardEvent(XK_Meta_L,NULL,SPI_KEY_RELEASE)) { |
| logMessage(LOG_WARNING, "meta release failed."); |
| ok = 0; |
| } |
| } else { |
| logMessage(LOG_WARNING, "meta press failed."); |
| } |
| |
| return ok; |
| } |
| } |
| |
| static void |
| scr_initialize (MainScreen *main) { |
| initializeRealScreen(main); |
| main->base.describe = describe_AtSpiScreen; |
| main->base.readCharacters = readCharacters_AtSpiScreen; |
| main->base.insertKey = insertKey_AtSpiScreen; |
| main->base.currentVirtualTerminal = currentVirtualTerminal_AtSpiScreen; |
| main->processParameters = processParameters_AtSpiScreen; |
| main->construct = construct_AtSpiScreen; |
| main->destruct = destruct_AtSpiScreen; |
| } |