#include "CtrlCore.h" #ifdef PLATFORM_X11 #include #endif NAMESPACE_UPP #ifdef PLATFORM_X11 #ifdef _DEBUG bool Ctrl::LogMessages = false ; #endif #define LLOG(x) // LOG(x) #define LTIMING(x) // RTIMING(x) #define LDUMP(x) // RDUMP(x) #define LDUMPC(x) // RDUMPC(x) // #define SYNCHRONIZE #define x_Event(x) { x, #x }, static struct XEventMap { int ID; const char *name; } sXevent[] = { #include "X11Event.i" 0, NULL }; ArrayMap& Ctrl::Xwindow() { return Single< ArrayMap >(); } Ptr Ctrl::WndCaretCtrl; int Ctrl::WndCaretTime; Rect Ctrl::WndCaretRect; bool Ctrl::WndCaretVisible; int Ctrl::Xbuttons; Window Ctrl::grabWindow, Ctrl::focusWindow; int Ctrl::Xeventtime; int Ctrl::PopupGrab; Ptr Ctrl::popupWnd; Point Ctrl::mousePos; static int s_starttime; void Ctrl::DoPaint(const Vector& invalid) { if(IsVisible()) { LTIMING("DoPaint"); fullrefresh = false; bool caret = false; if(WndCaretCtrl == this && WndCaretVisible) for(int i = 0; i < invalid.GetCount(); i++) if(invalid[i].Intersects(WndCaretRect)) { XorCaret(); caret = true; break; } if(GLX) return; GC gc = XCreateGC(Xdisplay, (Drawable)top->window, 0, 0); XftDraw *xftdraw = XftDrawCreate(Xdisplay, (Drawable) top->window, DefaultVisual(Xdisplay, Xscreenno), Xcolormap); Draw draw(top->window, gc, xftdraw, invalid); UpdateArea(draw, draw.GetClip()); XftDrawDestroy(xftdraw); XFreeGC(Xdisplay, gc); if(caret) XorCaret(); } } void Ctrl::WndScrollView(const Rect& r, int dx, int dy) { if(r.IsEmpty() || !GetWindow()) return; bool caret = false; if(WndCaretCtrl == this && WndCaretVisible && r.Intersects(WndCaretRect)) { caret = true; XorCaret(); } int cx = r.Width() - abs(dx); int cy = r.Height() - abs(dy); GC gc = XCreateGC(Xdisplay, GetWindow(), 0, 0); XCopyArea(Xdisplay, GetWindow(), GetWindow(), gc, r.left + max(-dx, 0), r.top + max(-dy, 0), cx, cy, r.left + max(dx, 0), r.top + max(dy, 0)); XFreeGC(Xdisplay, gc); XWindow *xw = GetXWindow(); Vector ur; if(xw) for(int i = 0; i < xw->invalid.GetCount(); i++) if(xw->invalid[i].Intersects(r)) ur.Add(xw->invalid[i]); for(int i = 0; i < ur.GetCount(); i++) Invalidate(*xw, ur[i].Offseted(dx, dy)); if(caret) XorCaret(); } bool Ctrl::IsWaitingEvent() { return XPending(Xdisplay); } Ctrl *Ctrl::CtrlFromWindow(Window w) { int q = Xwindow().Find(w); return q >= 0 ? Xwindow()[q].ctrl : NULL; } Ctrl::XWindow *Ctrl::GetXWindow() { if(!top) return NULL; int q = Xwindow().Find(top->window); return q >= 0 ? &Xwindow()[q] : NULL; } bool Ctrl::HookProc(XEvent *event) { return false; } void Ctrl::ProcessEvent(XEvent *event) { if(xim && XFilterEvent(event, None)) return; ArrayMap& xmap = Xwindow(); for(int i = 0; i < xmap.GetCount(); i++) if(xmap[i].ctrl && xmap[i].ctrl->HookProc(event)) return; FocusSync(); if(event->type == PropertyNotify && (event->xproperty.atom == XAtom("_QT_SETTINGS_TIMESTAMP_") || event->xproperty.atom == XAtom("_KDE_NET_USER_TIME"))) { LLOG("Property notify " << XAtomName(event->xproperty.atom) << " " << (void *)event->xany.window); for(int i = 0; i < Xwindow().GetCount(); i++) { Ctrl *q = Xwindow()[i].ctrl; if(q) q->Refresh(); } } if(event->type == SelectionRequest && event->xselectionrequest.owner == xclipboard().win && event->xselectionrequest.selection == XAtom("CLIPBOARD")) { xclipboard().Request(&event->xselectionrequest); return; } if(event->type == SelectionClear && event->xselectionclear.window == xclipboard().win && event->xselectionclear.selection == XAtom("CLIPBOARD")) { xclipboard().Clear(); return; } int q = xmap.Find(event->xany.window); #ifdef _DEBUG bool eo = false; if(LogMessages && event->type != NoExpose && event->type != PropertyNotify && event->type != MotionNotify) for(XEventMap *m = sXevent; m->ID; m++) if(m->ID == event->type) { int t = GetTickCount() - s_starttime; VppLog() << Format("%d.%03d", t / 1000, t % 1000); VppLog() << " EVENT " << Format("%-20.20s", m->name); VppLog() << "[window: " << event->xany.window << "] "; if(q >= 0) VppLog() << '<' << UPP::Name(Xwindow()[q].ctrl) << '>'; else VppLog() << " "; VppLog() << '\n'; eo = true; break; } #endif if(q < 0) { if(event->type == ButtonRelease && popupWnd) popupWnd->SetFocus(); return; } XWindow& w = xmap[q]; if(w.ctrl) { w.ctrl->EventProc(w, event); if(w.ctrl) w.ctrl->SyncMoves(); } else xmap.SetKey(q, None); DefferedFocusSync(); #ifdef _DEBUG #ifdef UPP_HEAP if(MemoryCheck) UPP::MemoryCheck(); #endif if(eo) LLOG("------ end of event processing ----------"); #endif } void Ctrl::TimerAndPaint() { LTIMING("TimerAndPaint"); AnimateCaret(); TimerProc(GetTickCount()); for(int i = 0; i < Xwindow().GetCount(); i++) { XWindow& xw = Xwindow()[i]; if(Xwindow().GetKey(i) && xw.exposed && xw.invalid.GetCount()) { if(xw.ctrl) { FocusSync(); xw.ctrl->SyncScroll(); Vector x = xw.invalid; xw.invalid.Clear(); xw.ctrl->DoPaint(x); } else Xwindow().SetKey(i, None); } } } bool Ctrl::ProcessEvent(bool *) { if(!IsWaitingEvent()) return false; XEvent event; XNextEvent(Xdisplay, &event); ProcessEvent(&event); return true; } void SweepMkImageCache(); bool Ctrl::ProcessEvents(bool *) { if(ProcessEvent()) { while(ProcessEvent()); TimerAndPaint(); SweepMkImageCache(); return true; } TimerAndPaint(); SweepMkImageCache(); return false; } void Ctrl::GuiSleep(int ms) { fd_set fdset; FD_ZERO(&fdset); FD_SET(Xconnection, &fdset); timeval timeout; timeout.tv_sec = ms / 1000; timeout.tv_usec = ms % 1000 * 1000; XFlush(Xdisplay); select(Xconnection + 1, &fdset, NULL, NULL, &timeout); } static int granularity = 10; void Ctrl::SetTimerGranularity(int ms) { granularity = ms; } void Ctrl::EventLoop(Ctrl *ctrl) { ASSERT(LoopLevel == 0 || ctrl); LoopLevel++; LLOG("Entering event loop at level " << LoopLevel << BeginIndent); Ctrl *ploop; if(ctrl) { ploop = LoopCtrl; LoopCtrl = ctrl; ctrl->inloop = true; } while(ctrl ? ctrl->InLoop() && ctrl->IsOpen() : GetTopCtrls().GetCount()) { XEvent event; fd_set fdset; FD_ZERO(&fdset); FD_SET(Xconnection, &fdset); timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 1000 * granularity; XFlush(Xdisplay); select(Xconnection + 1, &fdset, NULL, NULL, &timeout); // GuiSleep()(granularity); SyncMousePos(); while(IsWaitingEvent()) { LTIMING("XNextEvent"); XNextEvent(Xdisplay, &event); ProcessEvent(&event); } TimerAndPaint(); } if(ctrl) LoopCtrl = ploop; LoopLevel--; LLOG(EndIndent << "Leaving event loop "); } void Ctrl::SyncExpose() { if(!top) return; XEvent event; while(XCheckTypedWindowEvent(Xdisplay, top->window, Expose, &event)) ProcessEvent(&event); while(XCheckTypedWindowEvent(Xdisplay, top->window, GraphicsExpose, &event)) ProcessEvent(&event); } void Ctrl::Create(Ctrl *owner, bool redirect, bool savebits) { LLOG("Create " << Name() << " " << GetRect()); ASSERT(!IsChild() && !IsOpen()); LLOG("Ungrab1"); ReleaseGrab(); XSetWindowAttributes swa; swa.bit_gravity = ForgetGravity; swa.background_pixmap = None; swa.override_redirect = redirect; swa.save_under = savebits; Rect r = GetRect(); isopen = true; Window w = XCreateWindow(Xdisplay, RootWindow(Xdisplay, Xscreenno), r.left, r.top, r.Width(), r.Height(), 0, CopyFromParent, InputOutput, CopyFromParent, CWBackPixmap|CWBitGravity|CWSaveUnder|CWOverrideRedirect, &swa); if(!w) XError("XCreateWindow failed !"); int i = Xwindow().Find(None); if(i >= 0) Xwindow().SetKey(i, w); XWindow& cw = i >= 0 ? Xwindow()[i] : Xwindow().Add(w); cw.ctrl = this; cw.exposed = false; cw.owner = owner; cw.xic = xim ? XCreateIC(xim, XNInputStyle, XIMPreeditNothing|XIMStatusNothing, XNClientWindow, w, XNFocusWindow, w, NULL) : NULL; top = new Top; top->window = w; long im_event_mask = 0; if(cw.xic) XGetICValues(cw.xic, XNFilterEvents, &im_event_mask, NULL); XSelectInput(Xdisplay, w, ExposureMask|StructureNotifyMask|KeyPressMask| FocusChangeMask|KeyPressMask|KeyReleaseMask|PointerMotionMask| ButtonPressMask|ButtonReleaseMask|PropertyChangeMask| VisibilityChangeMask|im_event_mask); } void Ctrl::WndDestroy() { LLOG("WndDestroy " << Name()); if(!top || !isopen) return; AddGlobalRepaint(); bool revertfocus = HasWndFocus(); for(int i = 0; i < Xwindow().GetCount(); i++) { LOGBEGIN(); XWindow& w = Xwindow()[i]; if(Xwindow().GetKey(i) != None && w.owner == this && w.ctrl->IsOpen()) w.ctrl->WndDestroy(); LOGEND(); } Ptr owner; int i = Xwindow().Find(top->window); if(i >= 0) { XWindow& w = Xwindow()[i]; owner = w.owner; w.invalid.Clear(); if(w.xic) XDestroyIC(w.xic); } isopen = false; XDestroyWindow(Xdisplay, top->window); if(i >= 0) { Xwindow().SetKey(i, None); top->window = None; Xwindow()[i].ctrl = NULL; } if(revertfocus && owner) owner->TakeFocus(); delete top; top = NULL; } Vector Ctrl::GetTopCtrls() { Vector v; const ArrayMap& w = Xwindow(); for(int i = 0; i < w.GetCount(); i++) if(w.GetKey(i) && w[i].ctrl && !w[i].ctrl->parent) v.Add(w[i].ctrl); return v; } void Ctrl::StartPopupGrab() { if(PopupGrab == 0) { if(!top) return; if(XGrabPointer( Xdisplay, top->window, true, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime) == GrabSuccess) { PopupGrab++; popupWnd = GetTopWindow(); } } } void Ctrl::EndPopupGrab() { if(PopupGrab == 0) return; if(--PopupGrab == 0) { XUngrabPointer(Xdisplay, CurrentTime); XFlush(Xdisplay); } } void Ctrl::PopUp(Ctrl *owner, bool savebits, bool activate, bool, bool) { Ctrl *q = owner ? owner->GetTopCtrl() : GetActiveCtrl(); ignoretakefocus = true; Create(q, true, savebits); if(activate) { q->StartPopupGrab(); popupgrab = true; } if(top) popup = true; WndShow(visible); if(activate && IsEnabled()) SetFocus(); } Ctrl *Ctrl::GetActiveCtrl() { return focusCtrl ? focusCtrl->GetTopCtrl() : NULL; } bool Ctrl::IsAlphaSupported() { return false; } void Ctrl::SetAlpha(byte alpha) {} Ctrl *Ctrl::GetOwner() { if(!IsOpen()) return NULL; int q = Xwindow().Find(GetWindow()); return q >= 0 ? Xwindow()[q].owner : NULL; } void Ctrl::WndShow(bool b) { LLOG("WndShow " << b); if(top) { XWindowAttributes xwa; XGetWindowAttributes(Xdisplay, top->window, &xwa); bool v = xwa.map_state == IsViewable; if(b == v) return; if(b) XMapWindow(Xdisplay, top->window); else XUnmapWindow(Xdisplay, top->window); visible = b; StateH(SHOW); } } void Ctrl::WndUpdate() { LTIMING("WndUpdate"); LLOG("WNDUPDATE"); if(!top) return; SyncExpose(); XWindow& xw = Xwindow().Get(top->window); if(xw.exposed && xw.invalid.GetCount()) { SyncScroll(); DoPaint(xw.invalid); xw.invalid.Clear(); LTIMING("WndUpdate XSync"); XSync(Xdisplay, false); } } void Ctrl::WndUpdate(const Rect& r) { LTIMING("WndUpdate Rect"); LLOG("WNDUPDATE " << r); if(!top) return; SyncExpose(); XWindow& xw = Xwindow().Get(top->window); bool dummy; SyncScroll(); DoPaint(Intersect(xw.invalid, r, dummy)); LTIMING("WndUpdate XSync"); XSync(Xdisplay, false); xw.invalid = Subtract(xw.invalid, r, dummy); } void Ctrl::WndSetPos(const Rect& r) { if(!top) return; LLOG("WndSetPos " << Name() << r); AddGlobalRepaint(); XMoveResizeWindow(Xdisplay, top->window, r.left, r.top, r.Width(), r.Height()); rect = r; SetWndRect(r); } bool Ctrl::IsWndOpen() const { return top; } bool Ctrl::SetWndCapture() { if(!IsEnabled() || !top || !IsVisible()) return false; if(top->window == grabWindow) return true; int status = XGrabPointer( Xdisplay, top->window, false, ButtonPressMask|ButtonReleaseMask|PointerMotionMask|EnterWindowMask|LeaveWindowMask, GrabModeAsync, GrabModeAsync, None, None, CurrentTime ); if(status) return false; LLOG("Capture set ok"); grabWindow = top->window; return true; } bool Ctrl::HasWndCapture() const { return top && top->window == grabWindow; } void Ctrl::ReleaseGrab() { if(grabWindow) { XUngrabPointer(Xdisplay, CurrentTime); XFlush(Xdisplay); grabWindow = None; } } bool Ctrl::ReleaseWndCapture() { LLOG("Releasing capture"); if(top && top->window == grabWindow) { LLOG("Ungrab3"); ReleaseCapture(); return true; } return false; } static bool s_waitcursor; void Ctrl::SetMouseCursor(const Image& image) { if(!top) return; static Image img; static Cursor shc; static Window wnd; if(img.GetSerialId() != image.GetSerialId() || top->window != wnd) { img = image; Cursor hc = X11Cursor(img); wnd = top->window; XDefineCursor(Xdisplay, wnd, hc); if(shc) XFreeCursor(Xdisplay, shc); shc = hc; } } void WaitCursor::Show() { if(s_waitcursor) return; s_waitcursor = true; Ctrl::DoCursorShape(); //TODO - FIXIMAGE } WaitCursor::WaitCursor(bool show) { if(show) Show(); } WaitCursor::~WaitCursor() { if(s_waitcursor) { s_waitcursor = false; Ctrl::DoCursorShape(); } } void ClearKbdState_(); void Ctrl::TakeFocus() { if(IsChild()) { Ctrl *w = GetTopCtrl(); if(w) w->TakeFocus(); return; } XWindow *w = GetXWindow(); if(!w) return; if(ignoretakefocus) { LLOG("IGNORED TAKE_FOCUS (caused by CreateWindow)"); if(focusWindow != top->window) XSetInputFocus(Xdisplay, focusWindow, RevertToParent, CurrentTime); return; } if(w->owner) { LLOG("IGNORED TAKE_FOCUS (window is owned)"); return; } LLOG("TAKE_FOCUS " << Name()); ClearKbdState_(); if(IsEnabled() && IsVisible() && top->window != GetXServerFocusWindow()) SetWndFocus(); if(this != focusCtrlWnd) { if(IsEnabled()) { SetLastActive(w, this); LLOG("Activate " << Name()); SetFocusWnd(); } else { LLOG("Activate of disabled window" << Name()); if(w->last_active && w->last_active->IsOpen() && w->last_active->IsEnabled() && w->last_active != this) { LLOG(" activating last active: " << UPP::Name(w->last_active)); w->last_active->TakeFocus(); } } } return; } void Ctrl::KillFocus(Window window) { if(!window) return; int q = Xwindow().Find(window); if(q < 0) return; XWindow& w = Xwindow()[q]; if(w.ctrl) w.ctrl->KillFocusWnd(); } bool Ctrl::SetWndFocus() { LLOG("SetWndFocus " << Name()); if(top && top->window != focusWindow && IsEnabled() && IsVisible()) { LLOG("Setting focus... "); LTIMING("XSetInfputFocus"); KillFocus(focusWindow); XSetInputFocus(Xdisplay, top->window, RevertToParent, CurrentTime); focusWindow = top->window; SetFocusWnd(); return true; } return false; } bool Ctrl::HasWndFocus() const { return top && top->window == focusWindow; } Window Ctrl::GetXServerFocusWindow() { Window w; int dummy; XGetInputFocus(Xdisplay, &w, &dummy); return w; } void Ctrl::FocusSync() { Window fw = GetXServerFocusWindow(); if(fw != focusWindow) { LLOG("FocusSync to " << FormatIntHex(fw)); if(fw) { int q = Xwindow().Find(fw); if(q < 0) return; focusWindow = fw; XWindow& w = Xwindow()[q]; if(w.ctrl) { if(w.ctrl->IsPopUp()) return; KillFocus(focusWindow); focusWindow = None; w.ctrl->SetFocusWnd(); return; } } KillFocus(focusWindow); focusWindow = None; } } void Ctrl::XorCaret() { if(WndCaretCtrl && WndCaretCtrl->top && WndCaretCtrl->top->window) { LTIMING("XorCaret"); GC gc = XCreateGC(Xdisplay, WndCaretCtrl->top->window, 0, 0); XSetFunction(Xdisplay, gc, GXinvert); XFillRectangle(Xdisplay, WndCaretCtrl->top->window, gc, WndCaretRect.left, WndCaretRect.top, WndCaretRect.Width(), WndCaretRect.Height()); XFreeGC(Xdisplay, gc); } else WndCaretCtrl = NULL; } void Ctrl::AnimateCaret() { if(WndCaretCtrl && WndCaretCtrl->top) { int v = !(((GetTickCount() - WndCaretTime) / 500) & 1); if(v != WndCaretVisible) { XorCaret(); WndCaretVisible = v; } } } void Ctrl::WndDestroyCaret() { if(WndCaretVisible) XorCaret(); WndCaretCtrl = NULL; } void Ctrl::WndCreateCaret(const Rect& cr) { WndDestroyCaret(); WndCaretCtrl = this; WndCaretRect = cr - GetRect().TopLeft(); WndCaretVisible = true; WndCaretTime = GetTickCount(); XorCaret(); } void Ctrl::Invalidate(XWindow& xw, const Rect& _r) { LTIMING("Invalidate"); Vector inv; Rect r = _r; int ra = r.Width() * r.Height(); for(int i = 0; i < xw.invalid.GetCount(); i++) { const Rect& ir = xw.invalid[i]; int ia = ir.Width() * ir.Height(); Rect ur = r | ir; if(ur.Width() * ur.Height() < 2 * (ir.Width() * ir.Height() + ra)) r = ur; else if(!r.Contains(xw.invalid[i])) inv.Add(xw.invalid[i]); } Vector rs; rs.Add(r); for(int i = 0; i < inv.GetCount(); i++) { bool ch = false; Vector rs1 = Subtract(rs, inv[i], ch); if(ch) rs = rs1; } inv.AppendPick(rs); xw.invalid = inv; } void Ctrl::AddGlobalRepaint() { Rect rect = GetRect(); rect.Inflate(32, 32);//TODO !!! Not correct !!! should read frame extent instead... - or shift GraphicsExposes / NoExposes... ArrayMap& w = Xwindow(); for(int i = 0; i < w.GetCount(); i++) if(w.GetKey(i) && w[i].ctrl) { const Rect& r = w[i].ctrl->rect; if(rect.Intersects(r)) Invalidate(w[i], Rect(rect.Offseted(-r.TopLeft()))); } } void Ctrl::WndInvalidateRect(const Rect& r) { if(!top) return; LLOG("WndInvalidateRect " << r); Invalidate(Xwindow().Get(top->window), r); } void Ctrl::SetWndForeground() { LLOG("SetWndForeground " << Name()); if(!top || !IsVisible()) return; if(!IsEnabled()) { LLOG("Not enabled"); XWindow *w = GetXWindow(); if(w && w->last_active && w->last_active != this && w->last_active->IsOpen() && w->last_active->IsEnabled()) w->last_active->SetWndForeground(); } else { SetWndFocus(); XRaiseWindow(Xdisplay, top->window); } } bool Ctrl::IsWndForeground() const { const Ctrl *q = GetTopWindow(); return ~focusCtrlWnd == (q ? q : GetTopCtrl()); } bool Ctrl::WndEnable(bool b) { LLOG("WndEnable"); if(!top) return false; if(!b) { ReleaseCapture(); if(HasWndFocus()) XSetInputFocus(Xdisplay, None, RevertToPointerRoot, CurrentTime); } return true; } ViewDraw::ViewDraw(Ctrl *ctrl) { Ctrl *top = ctrl->GetTopCtrl(); Rect r = ctrl->GetScreenView() - top->GetScreenRect().TopLeft(); Vector clip; clip.Add(r); caret = Ctrl::WndCaretCtrl == ctrl && Ctrl::WndCaretVisible && r.Intersects(Ctrl::WndCaretRect); if(caret) Ctrl::XorCaret(); dw = top->GetWindow(); gc = XCreateGC(Xdisplay, dw, 0, 0); #ifdef PLATFORM_XFT xftdraw = XftDrawCreate(Xdisplay, (Drawable) dw, DefaultVisual(Xdisplay, Xscreenno), Xcolormap); #endif Init(clip, r.TopLeft()); } ViewDraw::~ViewDraw() { XFreeGC(Xdisplay, gc); if(caret) Ctrl::XorCaret(); } #endif END_UPP_NAMESPACE