adds support for accessx, makes it possible for people with disabilities to use rdesktop. Relies on XKB. Sync problem with changed modifiers on focus in not fixed yet...
git-svn-id: svn://svn.code.sf.net/p/rdesktop/code/trunk/rdesktop@51 423420c4-83ab-492f-b58f-81f9feb106b5
This commit is contained in:
parent
07baef6283
commit
7162923fbe
249
xwin.c
249
xwin.c
@ -2,17 +2,17 @@
|
|||||||
rdesktop: A Remote Desktop Protocol client.
|
rdesktop: A Remote Desktop Protocol client.
|
||||||
User interface services - X Window System
|
User interface services - X Window System
|
||||||
Copyright (C) Matthew Chapman 1999-2001
|
Copyright (C) Matthew Chapman 1999-2001
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
This program is free software; you can redistribute it and/or modify
|
||||||
it under the terms of the GNU General Public License as published by
|
it under the terms of the GNU General Public License as published by
|
||||||
the Free Software Foundation; either version 2 of the License, or
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU General Public License for more details.
|
GNU General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program; if not, write to the Free Software
|
along with this program; if not, write to the Free Software
|
||||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||||
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <X11/Xlib.h>
|
#include <X11/Xlib.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
|
#include <X11/XKBlib.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include "rdesktop.h"
|
#include "rdesktop.h"
|
||||||
@ -32,6 +33,7 @@ extern BOOL sendmotion;
|
|||||||
extern BOOL fullscreen;
|
extern BOOL fullscreen;
|
||||||
|
|
||||||
Display *display;
|
Display *display;
|
||||||
|
XkbDescPtr xkb;
|
||||||
static int x_socket;
|
static int x_socket;
|
||||||
static Window wnd;
|
static Window wnd;
|
||||||
static GC gc;
|
static GC gc;
|
||||||
@ -47,6 +49,17 @@ static BOOL xserver_be;
|
|||||||
static BOOL ownbackstore;
|
static BOOL ownbackstore;
|
||||||
static Pixmap backstore;
|
static Pixmap backstore;
|
||||||
|
|
||||||
|
/* needed to keep track of the modifiers */
|
||||||
|
static unsigned int key_modifier_state = 0;
|
||||||
|
static unsigned int key_down_state = 0;
|
||||||
|
|
||||||
|
#define DShift1Mask (1<<0)
|
||||||
|
#define DShift2Mask (1<<1)
|
||||||
|
#define DControl1Mask (1<<2)
|
||||||
|
#define DControl2Mask (1<<3)
|
||||||
|
#define DMod1Mask (1<<4)
|
||||||
|
#define DMod2Mask (1<<5)
|
||||||
|
|
||||||
#define FILL_RECTANGLE(x,y,cx,cy)\
|
#define FILL_RECTANGLE(x,y,cx,cy)\
|
||||||
{ \
|
{ \
|
||||||
XFillRectangle(display, wnd, gc, x, y, cx, cy); \
|
XFillRectangle(display, wnd, gc, x, y, cx, cy); \
|
||||||
@ -86,6 +99,9 @@ static int rop2_map[] = {
|
|||||||
#define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(display, gc, rop2_map[rop2]); }
|
#define SET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(display, gc, rop2_map[rop2]); }
|
||||||
#define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(display, gc, GXcopy); }
|
#define RESET_FUNCTION(rop2) { if (rop2 != ROP2_COPY) XSetFunction(display, gc, GXcopy); }
|
||||||
|
|
||||||
|
void xwin_release_modifiers(XKeyEvent* ev, uint32 ev_time, uint32 scancode);
|
||||||
|
void xwin_press_modifiers(XKeyEvent* ev, uint32 ev_time, uint32 scancode);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
translate8(uint8 *data, uint8 *out, uint8 *end)
|
translate8(uint8 *data, uint8 *out, uint8 *end)
|
||||||
{
|
{
|
||||||
@ -191,8 +207,44 @@ ui_create_window(char *title)
|
|||||||
Screen *screen;
|
Screen *screen;
|
||||||
uint16 test;
|
uint16 test;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
int xkb_minor, xkb_major;
|
||||||
|
int xkb_event, xkb_error, xkb_reason;
|
||||||
|
|
||||||
|
/* compare compiletime libs with runtime libs. */
|
||||||
|
xkb_major = XkbMajorVersion;
|
||||||
|
xkb_minor = XkbMinorVersion;
|
||||||
|
if( XkbLibraryVersion( &xkb_major, &xkb_minor ) == False )
|
||||||
|
{
|
||||||
|
error("please re-compile rdesktop\ncompile time version of xkb is not compatible with\nyour runtime version of the library\n");
|
||||||
|
return False;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* XKB is the 'new' keyboard handler in x.. ( the xkb code in Xfree86 originates from SGI, years 1993 and 1995 from what I could tell. )
|
||||||
|
* it makes it possible for people with disabilities to use rdesktop, stickykeys, bouncekeys etc. VERY MUCH useful.
|
||||||
|
* XFree86 has had support for it since it's earliest incarnation. I believe it is a reasonable dependency.
|
||||||
|
*/
|
||||||
|
display = XkbOpenDisplay( NULL, &xkb_event, &xkb_error, &xkb_major, &xkb_minor, &xkb_reason );
|
||||||
|
switch(xkb_reason)
|
||||||
|
{
|
||||||
|
case XkbOD_BadLibraryVersion:
|
||||||
|
error("XkbOD_BadLibraryVersion: XKB extensions in server and the library rdesktop is linked against aren't compatible with each other.\n");
|
||||||
|
break;
|
||||||
|
case XkbOD_ConnectionRefused:
|
||||||
|
error("XkbOD_ConnectionRefused\n");
|
||||||
|
break;
|
||||||
|
case XkbOD_BadServerVersion:
|
||||||
|
error("XkbOD_BadServerVersion\n");
|
||||||
|
break;
|
||||||
|
case XkbOD_NonXkbServer:
|
||||||
|
error("XkbOD_NonXkbServer: XKB extension not present in server\nupdate your X server.\n");
|
||||||
|
break;
|
||||||
|
case XkbOD_Success:
|
||||||
|
DEBUG("XkbOD_Success: Connection established with display\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
display = XOpenDisplay(NULL);
|
|
||||||
if (display == NULL)
|
if (display == NULL)
|
||||||
{
|
{
|
||||||
error("Failed to open display\n");
|
error("Failed to open display\n");
|
||||||
@ -203,7 +255,7 @@ ui_create_window(char *title)
|
|||||||
screen = DefaultScreenOfDisplay(display);
|
screen = DefaultScreenOfDisplay(display);
|
||||||
visual = DefaultVisualOfScreen(screen);
|
visual = DefaultVisualOfScreen(screen);
|
||||||
depth = DefaultDepthOfScreen(screen);
|
depth = DefaultDepthOfScreen(screen);
|
||||||
|
|
||||||
pfm = XListPixmapFormats(display, &i);
|
pfm = XListPixmapFormats(display, &i);
|
||||||
if (pfm != NULL)
|
if (pfm != NULL)
|
||||||
{
|
{
|
||||||
@ -284,10 +336,9 @@ ui_create_window(char *title)
|
|||||||
|
|
||||||
xkeymap_init();
|
xkeymap_init();
|
||||||
|
|
||||||
input_mask = KeyPressMask | KeyReleaseMask
|
input_mask = KeyPressMask | KeyReleaseMask |
|
||||||
| ButtonPressMask | ButtonReleaseMask
|
ButtonPressMask | ButtonReleaseMask |
|
||||||
| EnterWindowMask | LeaveWindowMask;
|
EnterWindowMask | LeaveWindowMask | KeymapStateMask;
|
||||||
|
|
||||||
if (sendmotion)
|
if (sendmotion)
|
||||||
input_mask |= PointerMotionMask;
|
input_mask |= PointerMotionMask;
|
||||||
|
|
||||||
@ -301,12 +352,31 @@ ui_create_window(char *title)
|
|||||||
backstore = XCreatePixmap(display, wnd, width, height, depth);
|
backstore = XCreatePixmap(display, wnd, width, height, depth);
|
||||||
|
|
||||||
XMapWindow(display, wnd);
|
XMapWindow(display, wnd);
|
||||||
|
|
||||||
|
/* TODO: error texts... make them friendly. */
|
||||||
|
xkb = XkbGetKeyboard(display, XkbAllComponentsMask, XkbUseCoreKbd);
|
||||||
|
if ((int)xkb == BadAlloc || xkb == NULL)
|
||||||
|
{
|
||||||
|
error( "XkbGetKeyboard failed.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* TODO: error texts... make them friendly. */
|
||||||
|
if( XkbSelectEvents(display, xkb->device_spec, XkbAllEventsMask, XkbAllEventsMask) == False )
|
||||||
|
{
|
||||||
|
error( "XkbSelectEvents failed.\n");
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
return True;
|
return True;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ui_destroy_window()
|
ui_destroy_window()
|
||||||
{
|
{
|
||||||
|
if( xkb != NULL )
|
||||||
|
XkbFreeKeyboard(xkb, XkbAllControlsMask, True);
|
||||||
|
|
||||||
if (ownbackstore)
|
if (ownbackstore)
|
||||||
XFreePixmap(display, backstore);
|
XFreePixmap(display, backstore);
|
||||||
|
|
||||||
@ -319,33 +389,54 @@ ui_destroy_window()
|
|||||||
static void
|
static void
|
||||||
xwin_process_events()
|
xwin_process_events()
|
||||||
{
|
{
|
||||||
XEvent event;
|
XkbEvent xkbevent;
|
||||||
|
|
||||||
KeySym keysym;
|
KeySym keysym;
|
||||||
uint8 scancode;
|
uint8 scancode;
|
||||||
uint16 button, flags;
|
uint16 button, flags;
|
||||||
uint32 ev_time;
|
uint32 ev_time;
|
||||||
|
uint32 tmpmods;
|
||||||
|
|
||||||
if (display == NULL)
|
if (display == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while (XCheckMaskEvent(display, ~0, &event))
|
while (XCheckMaskEvent(display, ~0, &xkbevent.core))
|
||||||
{
|
{
|
||||||
ev_time = time(NULL);
|
ev_time = time(NULL);
|
||||||
flags = 0;
|
flags = 0;
|
||||||
|
|
||||||
switch (event.type)
|
switch (xkbevent.type)
|
||||||
{
|
{
|
||||||
|
case KeymapNotify:
|
||||||
|
/* TODO:
|
||||||
|
* read modifier status at focus in, and update the local masks, and the other end as well..
|
||||||
|
* if not, we may get out of sync.
|
||||||
|
* xkbevent.core.xkeymap.key_vector
|
||||||
|
* char key_vector[32];
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
case KeyRelease:
|
case KeyRelease:
|
||||||
flags = KBD_FLAG_DOWN | KBD_FLAG_UP;
|
flags = KBD_FLAG_DOWN | KBD_FLAG_UP;
|
||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
case KeyPress:
|
case KeyPress:
|
||||||
keysym = XKeycodeToKeysym(display, event.xkey.keycode, 0);
|
if( XkbTranslateKeyCode(xkb, xkbevent.core.xkey.keycode, xkbevent.core.xkey.state, &tmpmods, &keysym) == False )
|
||||||
scancode = xkeymap_translate_key(keysym, event.xkey.keycode, &flags);
|
break;
|
||||||
if (scancode == 0)
|
scancode = xkeymap_translate_key(keysym, xkbevent.core.xkey.keycode, &flags);
|
||||||
|
|
||||||
|
if (scancode == 0 )
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/* keep track of the modifiers -- needed for stickykeys... */
|
||||||
|
if( xkbevent.type == KeyPress )
|
||||||
|
xwin_press_modifiers( &xkbevent.core.xkey, ev_time, scancode );
|
||||||
|
|
||||||
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, flags, scancode, 0);
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, flags, scancode, 0);
|
||||||
|
|
||||||
|
if( xkbevent.type == KeyRelease )
|
||||||
|
xwin_release_modifiers( &xkbevent.core.xkey, ev_time, scancode );
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ButtonPress:
|
case ButtonPress:
|
||||||
@ -353,21 +444,21 @@ xwin_process_events()
|
|||||||
/* fall through */
|
/* fall through */
|
||||||
|
|
||||||
case ButtonRelease:
|
case ButtonRelease:
|
||||||
button = xkeymap_translate_button(event.xbutton.button);
|
button = xkeymap_translate_button(xkbevent.core.xbutton.button);
|
||||||
if (button == 0)
|
if (button == 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
rdp_send_input(ev_time, RDP_INPUT_MOUSE,
|
rdp_send_input(ev_time, RDP_INPUT_MOUSE,
|
||||||
flags | button,
|
flags | button,
|
||||||
event.xbutton.x,
|
xkbevent.core.xbutton.x,
|
||||||
event.xbutton.y);
|
xkbevent.core.xbutton.y);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case MotionNotify:
|
case MotionNotify:
|
||||||
rdp_send_input(ev_time, RDP_INPUT_MOUSE,
|
rdp_send_input(ev_time, RDP_INPUT_MOUSE,
|
||||||
MOUSE_FLAG_MOVE,
|
MOUSE_FLAG_MOVE,
|
||||||
event.xmotion.x,
|
xkbevent.core.xmotion.x,
|
||||||
event.xmotion.y);
|
xkbevent.core.xmotion.y);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EnterNotify:
|
case EnterNotify:
|
||||||
@ -381,14 +472,122 @@ xwin_process_events()
|
|||||||
|
|
||||||
case Expose:
|
case Expose:
|
||||||
XCopyArea(display, backstore, wnd, gc,
|
XCopyArea(display, backstore, wnd, gc,
|
||||||
event.xexpose.x, event.xexpose.y,
|
xkbevent.core.xexpose.x, xkbevent.core.xexpose.y,
|
||||||
event.xexpose.width, event.xexpose.height,
|
xkbevent.core.xexpose.width, xkbevent.core.xexpose.height,
|
||||||
event.xexpose.x, event.xexpose.y);
|
xkbevent.core.xexpose.x, xkbevent.core.xexpose.y);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
xwin_release_modifiers(XKeyEvent* ev, uint32 ev_time, uint32 scancode)
|
||||||
|
{
|
||||||
|
switch (scancode) {
|
||||||
|
case 0x2a:
|
||||||
|
key_down_state &= ~DShift1Mask;
|
||||||
|
break;
|
||||||
|
case 0x36:
|
||||||
|
key_down_state &= ~DShift2Mask;
|
||||||
|
break;
|
||||||
|
case 0x1d:
|
||||||
|
key_down_state &= ~DControl1Mask;
|
||||||
|
break;
|
||||||
|
case 0x9d:
|
||||||
|
key_down_state &= ~DControl2Mask;
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
key_down_state &= ~DMod1Mask;
|
||||||
|
break;
|
||||||
|
case 0xb8:
|
||||||
|
key_down_state &= ~DMod2Mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !(ShiftMask & ev->state) && (key_down_state & DShift1Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_UP, 0x2a, 0);
|
||||||
|
key_down_state &= ~DShift1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !(ControlMask & ev->state) && (key_down_state & DControl1Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_UP, 0x1d, 0);
|
||||||
|
key_down_state &= ~DControl1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !(Mod1Mask & ev->state) && (key_down_state & DMod1Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_UP, 0x38, 0);
|
||||||
|
key_down_state &= ~DMod1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( !(Mod2Mask & ev->state) && (key_down_state & DMod2Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_UP, 0xb8, 0);
|
||||||
|
key_down_state &= ~DMod2Mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
xwin_press_modifiers(XKeyEvent* ev, uint32 ev_time, uint32 scancode)
|
||||||
|
{
|
||||||
|
key_modifier_state = ev->state;
|
||||||
|
|
||||||
|
switch (scancode) {
|
||||||
|
case 0x2a:
|
||||||
|
key_down_state |= DShift1Mask;
|
||||||
|
break;
|
||||||
|
case 0x36:
|
||||||
|
key_down_state |= DShift2Mask;
|
||||||
|
break;
|
||||||
|
case 0x1d:
|
||||||
|
key_down_state |= DControl1Mask;
|
||||||
|
break;
|
||||||
|
case 0x9d:
|
||||||
|
key_down_state |= DControl2Mask;
|
||||||
|
break;
|
||||||
|
case 0x38:
|
||||||
|
key_down_state |= DMod1Mask;
|
||||||
|
break;
|
||||||
|
case 0xb8:
|
||||||
|
key_down_state |= DMod2Mask;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (ShiftMask & ev->state) && !((key_down_state & DShift1Mask) || (key_down_state & DShift2Mask)))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_DOWN, 0x2a, 0);
|
||||||
|
key_down_state |= DShift1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (ControlMask & ev->state) && !((key_down_state & DControl1Mask) || (key_down_state & DControl2Mask)))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_DOWN, 0x1d, 0);
|
||||||
|
key_down_state |= DControl1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (Mod1Mask & ev->state) && !(key_down_state & DMod1Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_DOWN, 0x38, 0);
|
||||||
|
key_down_state |= DMod1Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( (Mod2Mask & ev->state) && !(key_down_state & DMod2Mask))
|
||||||
|
{
|
||||||
|
rdp_send_input(ev_time, RDP_INPUT_SCANCODE, KBD_FLAG_DOWN, 0xb8, 0);
|
||||||
|
key_down_state |= DMod2Mask;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ui_select(int rdp_socket)
|
ui_select(int rdp_socket)
|
||||||
{
|
{
|
||||||
@ -923,7 +1122,7 @@ ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,
|
|||||||
if (entry != NULL) {
|
if (entry != NULL) {
|
||||||
if ((((uint8 *) (entry->data))[1] == 0)
|
if ((((uint8 *) (entry->data))[1] == 0)
|
||||||
&& (!(flags & TEXT2_IMPLICIT_X))) {
|
&& (!(flags & TEXT2_IMPLICIT_X))) {
|
||||||
if (flags & TEXT2_VERTICAL)
|
if (flags & TEXT2_VERTICAL)
|
||||||
y += text[i + 2];
|
y += text[i + 2];
|
||||||
else
|
else
|
||||||
x += text[i + 2];
|
x += text[i + 2];
|
||||||
@ -932,7 +1131,7 @@ ui_draw_text(uint8 font, uint8 flags, int mixmode, int x, int y,
|
|||||||
i += 3;
|
i += 3;
|
||||||
else
|
else
|
||||||
i += 2;
|
i += 2;
|
||||||
length -= i;
|
length -= i;
|
||||||
/* this will move pointer from start to first character after FE command */
|
/* this will move pointer from start to first character after FE command */
|
||||||
text = &(text[i]);
|
text = &(text[i]);
|
||||||
i = 0;
|
i = 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user