mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
435 lines
11 KiB
C++
435 lines
11 KiB
C++
#include "CtrlCore.h"
|
|
|
|
#ifdef GUI_X11
|
|
|
|
namespace Upp {
|
|
|
|
#define LLOG(x) //DLOG(x)
|
|
|
|
#define LTIMING(x) //TIMING(x)
|
|
|
|
static dword sKbdState;
|
|
static dword sModState;
|
|
|
|
void ClearKbdState_()
|
|
{
|
|
GuiLock __;
|
|
sKbdState = 0;
|
|
}
|
|
|
|
Point GetMousePos()
|
|
{
|
|
GuiLock __;
|
|
LTIMING("GetMousePos");
|
|
return Ctrl::mousePos;
|
|
}
|
|
|
|
void Ctrl::SyncMousePos()
|
|
{
|
|
GuiLock __;
|
|
LTIMING("XQueryPointer");
|
|
int x, y, xx, yy;
|
|
Window dm1, dm2;
|
|
Ctrl::mousePos = Null;
|
|
if(XQueryPointer(Xdisplay, Xroot, &dm1, &dm2, &x, &y, &xx, &yy, &sKbdState))
|
|
Ctrl::mousePos = Point(x, y);
|
|
}
|
|
|
|
bool GetShift() { GuiLock __; return sKbdState & ShiftMask; }
|
|
bool GetCtrl() { GuiLock __; return sKbdState & ControlMask; }
|
|
bool GetAlt() { GuiLock __; return sKbdState & Mod1Mask; }
|
|
bool GetCapsLock() { GuiLock __; return sKbdState & LockMask; }
|
|
bool GetMouseLeft() { GuiLock __; return sModState & Button1Mask; }
|
|
bool GetMouseRight() { GuiLock __; return sModState & (Ctrl::Xbuttons >= 3 ? Button3Mask : Button2Mask); }
|
|
bool GetMouseMiddle() { GuiLock __; return sModState & (Ctrl::Xbuttons >= 3 ? Button2Mask : 0); }
|
|
|
|
int Ctrl::Xbuttontime;
|
|
Point Ctrl::Xbuttonpos;
|
|
|
|
dword Ctrl::KEYtoK(dword key)
|
|
{
|
|
if(key != K_CTRL_KEY && key != K_SHIFT_KEY && key != K_ALT_KEY) {
|
|
if(GetCtrl()) key |= K_CTRL;
|
|
if(GetAlt()) key |= K_ALT;
|
|
if(GetShift()) key |= K_SHIFT;
|
|
}
|
|
return key;
|
|
}
|
|
|
|
void Ctrl::SetLastActive(XWindow *w, Ctrl *la)
|
|
{
|
|
GuiLock __;
|
|
while(w) {
|
|
LLOG(" to " << UPP::Name(w->ctrl));
|
|
w->last_active = la;
|
|
w = w->owner ? w->owner->GetXWindow() : NULL;
|
|
}
|
|
}
|
|
|
|
void Ctrl::EventProc(XWindow& w, XEvent *event)
|
|
{
|
|
GuiLock __;
|
|
eventid++;
|
|
Ptr<Ctrl> _this = this;
|
|
bool pressed = false;
|
|
int count = 1;
|
|
switch(event->type) {
|
|
case NoExpose:
|
|
LLOG("NoExpose serial " << event->xnoexpose.serial);
|
|
break;
|
|
case GraphicsExpose:
|
|
LLOG("GraphicsExpose serial " << event->xgraphicsexpose.serial);
|
|
case Expose: {
|
|
XExposeEvent& e = event->xexpose;
|
|
w.exposed = true;
|
|
LLOG("Expose " << RectC(e.x, e.y, e.width, e.height));
|
|
Invalidate(w, RectC(e.x, e.y, e.width, e.height));
|
|
}
|
|
return;
|
|
case ConfigureNotify: {
|
|
XConfigureEvent& e = event->xconfigure;
|
|
int x, y;
|
|
Window dummy;
|
|
Window xwin = GetWindow();
|
|
if(xwin) {
|
|
Window DestW = (GetParent() ? GetParentWindow() : Xroot);
|
|
XTranslateCoordinates(Xdisplay, xwin, DestW, 0, 0, &x, &y, &dummy);
|
|
Rect rect = RectC(x, y, e.width, e.height);
|
|
LLOG("CongigureNotify " << rect);
|
|
if(GetRect() != rect)
|
|
SetWndRect(rect);
|
|
}
|
|
// Synchronizes native windows (NOT the main one)
|
|
SyncNativeWindows();
|
|
}
|
|
return;
|
|
default:
|
|
if(!IsEnabled()) return;
|
|
}
|
|
LTIMING("XUserInput");
|
|
switch(event->type) {
|
|
case FocusIn:
|
|
xclipboard().InvalidateFormats();
|
|
if(w.xic)
|
|
XSetICFocus(w.xic);
|
|
break;
|
|
case FocusOut:
|
|
xclipboard().InvalidateFormats();
|
|
if(w.xic)
|
|
XUnsetICFocus(w.xic);
|
|
break;
|
|
case KeyPress: {
|
|
pressed = true;
|
|
LLOG("event type:" << event->type << " state:" << event->xkey.state <<
|
|
"keycode:" << event->xkey.keycode);
|
|
Vector<XEvent> events;
|
|
while(IsWaitingEvent())
|
|
XNextEvent(Xdisplay, &events.Add());
|
|
auto SameKey = [&](XEvent& ev) {
|
|
return ev.xkey.state == event->xkey.state && ev.xkey.keycode == event->xkey.keycode;
|
|
};
|
|
int from = 0;
|
|
for(;;) {
|
|
int up_i = -1;
|
|
for(int i = from; i < events.GetCount(); i++) {
|
|
XEvent& ev = events[i];
|
|
if(ev.type == KeyRelease) {
|
|
if(SameKey(ev))
|
|
up_i = i;
|
|
break;
|
|
}
|
|
}
|
|
if(up_i < 0)
|
|
break;
|
|
int down_i = -1;
|
|
for(int i = up_i + 1; i < events.GetCount(); i++) {
|
|
XEvent& ev = events[i];
|
|
if(ev.type == KeyRelease) {
|
|
if(SameKey(ev))
|
|
down_i = i;
|
|
break;
|
|
}
|
|
}
|
|
if(down_i < 0)
|
|
break;
|
|
count++;
|
|
from = down_i + 1;
|
|
events[up_i].type = events[down_i].type = -1;
|
|
}
|
|
for(int i = events.GetCount() - 1; i >= 0; i--) {
|
|
XEvent& ev = events[i];
|
|
if(ev.type >= 0)
|
|
XPutBackEvent(Xdisplay, &ev);
|
|
}
|
|
}
|
|
case KeyRelease: {
|
|
mousePos = Point(event->xkey.x_root, event->xkey.y_root);
|
|
char buff[128];
|
|
Xeventtime = event->xkey.time;
|
|
LLOG("Key Xeventtime: " << Xeventtime << " count:" << count);
|
|
KeySym keysym;
|
|
int chr = 0;
|
|
WString wtext;
|
|
if(pressed && w.xic) {
|
|
Status status;
|
|
int len = Xutf8LookupString(w.xic, &event->xkey, buff, sizeof(buff), &keysym, &status);
|
|
buff[len] = 0;
|
|
if(status == XLookupChars || status == XLookupBoth) {
|
|
chr = ToUtf32(buff, len)[0];
|
|
if(status == XLookupChars)
|
|
wtext = ToUtf32(buff, len);
|
|
}
|
|
else
|
|
if(status != XLookupKeySym && status != XLookupBoth)
|
|
keysym = 0;
|
|
}
|
|
else {
|
|
int len = XLookupString(&event->xkey, buff, sizeof(buff), &keysym, NULL);
|
|
buff[len] = 0;
|
|
chr = ToUtf32(buff, len)[0];
|
|
if(len > 1)
|
|
wtext = ToUtf32(buff, len);
|
|
}
|
|
if(keysym == XK_Control_L || keysym == XK_Control_R) {
|
|
keysym = XK_Control_L;
|
|
if(pressed)
|
|
sKbdState |= ControlMask;
|
|
else
|
|
sKbdState &= ~ControlMask;
|
|
}
|
|
if(keysym == XK_Shift_L || keysym == XK_Shift_R) {
|
|
keysym = XK_Shift_L;
|
|
if(pressed)
|
|
sKbdState |= ShiftMask;
|
|
else
|
|
sKbdState &= ~ShiftMask;
|
|
}
|
|
if(keysym == XK_Meta_L || keysym == XK_Meta_R || keysym == XK_Alt_L ||
|
|
keysym == XK_Alt_R || keysym == XK_Super_L || keysym == XK_Super_R ||
|
|
keysym == XK_Hyper_L || keysym == XK_Hyper_R || keysym == XK_ISO_Prev_Group) {
|
|
keysym = XK_Meta_L;
|
|
if(pressed)
|
|
sKbdState |= Mod1Mask;
|
|
else
|
|
sKbdState &= ~Mod1Mask;
|
|
}
|
|
LLOG("KeySym:" << FormatIntHex(keysym) << " " << (char)keysym << " " << count);
|
|
dword up = pressed ? 0 : K_KEYUP;
|
|
static struct {
|
|
KeySym keysym;
|
|
dword key;
|
|
}
|
|
keypadtab[] = {
|
|
{ XK_ISO_Left_Tab, K_TAB|K_SHIFT },
|
|
{ XK_BackSpace, K_BACKSPACE },
|
|
{ XK_Tab, K_TAB },
|
|
{ XK_Return, K_ENTER },
|
|
{ XK_KP_Enter, K_ENTER },
|
|
{ XK_Escape, K_ESCAPE },
|
|
{ XK_space, K_SPACE },
|
|
{ XK_KP_Space, K_SPACE },
|
|
{ XK_KP_Tab, K_TAB },
|
|
{ XK_KP_Enter, K_ENTER },
|
|
{ XK_KP_F1, K_F1 },
|
|
{ XK_KP_F2, K_F2 },
|
|
{ XK_KP_F3, K_F3 },
|
|
{ XK_KP_F4, K_F4 },
|
|
{ XK_KP_Home, K_HOME },
|
|
{ XK_KP_Left, K_LEFT },
|
|
{ XK_KP_Up, K_UP },
|
|
{ XK_KP_Right, K_RIGHT },
|
|
{ XK_KP_Down, K_DOWN },
|
|
{ XK_KP_Page_Up, K_PAGEUP },
|
|
{ XK_KP_Page_Down, K_PAGEDOWN },
|
|
{ XK_KP_End, K_END },
|
|
{ XK_KP_Begin, K_HOME },
|
|
{ XK_KP_Insert, K_INSERT },
|
|
{ XK_KP_Delete, K_DELETE },
|
|
},
|
|
numpadtab[] = {
|
|
{ XK_KP_0, K_NUMPAD0 },
|
|
{ XK_KP_1, K_NUMPAD1 },
|
|
{ XK_KP_2, K_NUMPAD2 },
|
|
{ XK_KP_3, K_NUMPAD3 },
|
|
{ XK_KP_4, K_NUMPAD4 },
|
|
{ XK_KP_5, K_NUMPAD5 },
|
|
{ XK_KP_6, K_NUMPAD6 },
|
|
{ XK_KP_7, K_NUMPAD7 },
|
|
{ XK_KP_8, K_NUMPAD8 },
|
|
{ XK_KP_9, K_NUMPAD9 },
|
|
{ XK_KP_Multiply, K_MULTIPLY },
|
|
{ XK_KP_Add, K_ADD },
|
|
{ XK_KP_Separator, K_SEPARATOR },
|
|
{ XK_KP_Subtract, K_SUBTRACT },
|
|
{ XK_KP_Decimal, K_DECIMAL },
|
|
{ XK_KP_Divide, K_DIVIDE },
|
|
};
|
|
for(int i = 0; i < __countof(keypadtab); i++) {
|
|
if(keypadtab[i].keysym == keysym) {
|
|
DispatchKey(KEYtoK(keypadtab[i].key|up), count);
|
|
return;
|
|
}
|
|
}
|
|
if(GetCtrl() || GetAlt()) // Send Numpad keycodes.
|
|
for(int i = 0; i < __countof(numpadtab); i++) {
|
|
if(numpadtab[i].keysym == keysym) {
|
|
DispatchKey(KEYtoK(numpadtab[i].key|up), count);
|
|
return;
|
|
}
|
|
}
|
|
if(GetShift() && chr == 0) {
|
|
static dword k[] = { 41, 33, 64, 35, 36, 37, 94, 38, 42, 40 };
|
|
for(int i = 0; i < 10; i++)
|
|
if(keysym == k[i]) {
|
|
DispatchKey(KEYtoK(i + K_0)|up, count);
|
|
return;
|
|
}
|
|
}
|
|
if(GetCtrl() || GetAlt()) { // fix Ctrl+Shift+1 etc...
|
|
keysym = decode((int)event->xkey.keycode,
|
|
0x0a, 0x31,
|
|
0x0b, 0x32,
|
|
0x0c, 0x33,
|
|
0x0d, 0x34,
|
|
0x0e, 0x35,
|
|
0x0f, 0x36,
|
|
0x10, 0x37,
|
|
0x11, 0x38,
|
|
0x12, 0x39,
|
|
0x13, 0x30, keysym);
|
|
}
|
|
// DLOG("keysym: " << keysym << " " << (char)keysym);
|
|
if(keysym >= '0' && keysym <= '9' && (chr == 0 || GetCtrl() || GetAlt())) {
|
|
DispatchKey(KEYtoK(keysym - '0' + K_0)|up, count);
|
|
return;
|
|
}
|
|
if(chr >= 1 && chr < 32) {
|
|
DispatchKey(KEYtoK(chr - 1 + K_CTRL_A)|up, count);
|
|
return;
|
|
}
|
|
if(keysym >= 0xff80 && keysym <= 0xffb9 && chr) {
|
|
DispatchKey(KEYtoK(chr)|up, count);
|
|
return;
|
|
}
|
|
if(keysym >= 0xff00 && chr < 128 ||
|
|
(GetCtrl() || GetAlt()) && keysym >= 0x20 && keysym < 0x7f) {
|
|
if(keysym >= 'a' && keysym <= 'z')
|
|
keysym = keysym - 'a' + 'A';
|
|
DispatchKey(KEYtoK(keysym|K_DELTA)|up, count);
|
|
return;
|
|
}
|
|
|
|
if((chr == 32 || chr == 9 || chr == 13) && !pressed)
|
|
DispatchKey(chr|K_KEYUP, count);
|
|
if(chr && pressed) {
|
|
DispatchKey(chr, count);
|
|
for(int ii = 1; ii < wtext.GetLength(); ii++)
|
|
DispatchKey(wtext[ii], count);
|
|
}
|
|
}
|
|
break;
|
|
case ButtonPress: {
|
|
if(!HasWndFocus() && !popup)
|
|
SetWndFocus();
|
|
ClickActivateWnd();
|
|
mousePos = Point(event->xbutton.x_root, event->xbutton.y_root);
|
|
ReleaseGrab();
|
|
XButtonEvent& e = event->xbutton;
|
|
sModState = e.state;
|
|
Xeventtime = e.time;
|
|
if(ignoreclick) break;
|
|
Point p = Point(e.x, e.y);
|
|
dword action = DOWN;
|
|
if((dword)e.time - (dword)Xbuttontime < 800) {
|
|
action = DOUBLE;
|
|
Xbuttontime = Xeventtime - 0x80000000;
|
|
}
|
|
else {
|
|
Xbuttontime = e.time;
|
|
Xbuttonpos = mousePos;
|
|
}
|
|
switch(e.button) {
|
|
case Button1:
|
|
sModState |= Button1Mask;
|
|
DispatchMouse(LEFT|action, p, 0);
|
|
break;
|
|
case Button2:
|
|
sModState |= Button2Mask;
|
|
if(Xbuttons < 3)
|
|
DispatchMouse(RIGHT|action, p, 0);
|
|
else
|
|
DispatchMouse(MIDDLE|action, p, 0);
|
|
break;
|
|
case Button3:
|
|
sModState |= Button3Mask;
|
|
DispatchMouse(RIGHT|action, p, 0);
|
|
break;
|
|
}
|
|
if(_this) PostInput();
|
|
}
|
|
break;
|
|
case ButtonRelease: {
|
|
mousePos = Point(event->xbutton.x_root, event->xbutton.y_root);
|
|
XButtonEvent& e = event->xbutton;
|
|
sModState = e.state;
|
|
Xeventtime = e.time;
|
|
Point p = Point(e.x, e.y);
|
|
switch(e.button) {
|
|
case Button1:
|
|
sModState &= ~Button1Mask;
|
|
break;
|
|
case Button2:
|
|
sModState &= ~Button2Mask;
|
|
break;
|
|
case Button3:
|
|
sModState &= ~Button3Mask;
|
|
break;
|
|
}
|
|
if(ignoreclick)
|
|
EndIgnore();
|
|
else
|
|
switch(e.button) {
|
|
case Button1:
|
|
DispatchMouse(LEFTUP, p, 0);
|
|
break;
|
|
case Button2:
|
|
if(Xbuttons < 3)
|
|
DispatchMouse(RIGHTUP, p, 0);
|
|
else
|
|
DispatchMouse(MIDDLEUP, p, 0);
|
|
break;
|
|
case Button3:
|
|
DispatchMouse(RIGHTUP, p, 0);
|
|
break;
|
|
case Button4:
|
|
DispatchMouse(MOUSEWHEEL, p, 120);
|
|
break;
|
|
case Button5:
|
|
DispatchMouse(MOUSEWHEEL, p, -120);
|
|
break;
|
|
}
|
|
if(_this) PostInput();
|
|
}
|
|
break;
|
|
case MotionNotify:
|
|
Window xwin = GetWindow();
|
|
if(xwin) {
|
|
while(XCheckWindowEvent(Xdisplay, xwin, PointerMotionMask, event));
|
|
EndIgnore();
|
|
mousePos = Point(event->xmotion.x_root, event->xmotion.y_root);
|
|
Xeventtime = event->xmotion.time;
|
|
Point p = mousePos - Xbuttonpos;
|
|
if(max(abs(p.x), abs(p.y)) > 4)
|
|
Xbuttontime = Xeventtime - 0x80000000;
|
|
sModState = event->xmotion.state;
|
|
DispatchMouse(MOUSEMOVE, Point(event->xmotion.x, event->xmotion.y));
|
|
DoCursorShape();
|
|
}
|
|
break;
|
|
}
|
|
DropEvent(w, event);
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|