// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef toolx_X11_base_session #define toolx_X11_base_session // pure X11 code, no GL. #include #include #include "dispatcher" #include #include //XA_INTEGER #include //XVisualInfo namespace toolx { namespace X11 { class base_session { public: base_session(std::ostream& a_out,unsigned int a_monitor = 0) :m_out(a_out) ,m_monitor(a_monitor) ,m_display(0) ,m_WM_DELETE_WINDOW_atom(None) ,m_SESSION_EXIT_STEER_atom(None) { //NOTE : macOS : XOpenDisplay leaks 128 bytes. m_display = ::XOpenDisplay(NULL); if(!m_display) { m_out << "toolx::X11::base_session::base_session : can't open display." << std::endl; return; } int monitors = ::XScreenCount(m_display); if(int(m_monitor)>=monitors) { m_out << "toolx::X11::base_session::base_session : bad monitor index " << m_monitor << ". (#monitors " << monitors << ")." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return; } m_WM_DELETE_WINDOW_atom = ::XInternAtom(m_display,"WM_DELETE_WINDOW",False); if(m_WM_DELETE_WINDOW_atom==None) { m_out << "toolx::X11::base_session::base_session : can't create WM_DELETE_WINDOW Atom." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return; } m_SESSION_EXIT_STEER_atom = ::XInternAtom(m_display,"TOOLX_X11_SESSION_EXIT_STEER",False); if(m_SESSION_EXIT_STEER_atom==None) { m_out << "toolx::X11::base_session::base_session :" << " can't create TOOLX_X11_SESSION_EXIT_STEER Atom." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return; } } virtual ~base_session() { clear_dispatchers(); if(m_display) ::XCloseDisplay(m_display); m_display = 0; } protected: base_session(const base_session& a_from) :m_out(a_from.m_out) ,m_monitor(a_from.m_monitor) ,m_display(0) ,m_WM_DELETE_WINDOW_atom(None) ,m_SESSION_EXIT_STEER_atom(None) {} base_session& operator=(const base_session& a_from){ if(&a_from==this) return *this; return *this; } public: std::ostream& out() const {return m_out;} Atom WM_DELETE_WINDOW_atom() const {return m_WM_DELETE_WINDOW_atom;} Atom SESSION_EXIT_STEER_atom() const {return m_SESSION_EXIT_STEER_atom;} bool is_valid() const {return m_display?true:false;} unsigned int monitor() const {return m_monitor;} Display* display() const {return m_display;} bool steer() { if(!m_display) return false; while(true) { XEvent xevent; ::XNextEvent(m_display,&xevent); if(xevent.type==ClientMessage) { if(xevent.xclient.data.l[0]==(long)m_SESSION_EXIT_STEER_atom) break; } dispatch(xevent); } return true; } bool sync() { if(!m_display) return false; ::XSync(m_display,False); while(true) { XEvent xevent; if(::XPending(m_display)) { ::XNextEvent(m_display,&xevent); if(xevent.type==ClientMessage) { if(xevent.xclient.data.l[0]==(long)m_SESSION_EXIT_STEER_atom) return false; } dispatch(xevent); } else { break; } } return true; } bool event_pending(bool& a_is) { if(!m_display) {a_is = false;return false;} a_is = ::XPending(m_display)?true:false; return true; } bool next_event(bool& a_to_exit) { if(!m_display) {a_to_exit = false;return false;} XEvent xevent; ::XNextEvent(m_display,&xevent); if(xevent.type==ClientMessage) { if(xevent.xclient.data.l[0]==(long)m_SESSION_EXIT_STEER_atom) { a_to_exit = true; return true; } } dispatch(xevent); a_to_exit = false; return true; } bool flush() { if(!m_display) return false; ::XFlush(m_display); return true; } ////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// Window create_window(const char* a_title,int a_x,int a_y,unsigned int a_width,unsigned int a_height) { if(!m_display) return 0L; XSetWindowAttributes swa; swa.event_mask = StructureNotifyMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask | KeyPressMask; swa.background_pixel = ::XWhitePixel(m_display,m_monitor); Window window = ::XCreateWindow(m_display, ::XRootWindow(m_display,m_monitor), a_x,a_y,a_width,a_height, 0, (int)CopyFromParent, InputOutput, (Visual*)CopyFromParent, CWEventMask|CWBackPixel,&swa); if(window==0L) { m_out << "toolx::X11::base_session::create_window : can't create a X11 window." << std::endl; return 0L; } XTextProperty tp; ::XStringListToTextProperty((char**)&a_title,1,&tp); XSizeHints sh; sh.flags = USPosition | USSize; ::XSetWMProperties(m_display,window,&tp,&tp,0,0,&sh,0,0); ::XFree(tp.value); ::XSetWMProtocols(m_display,window,&m_WM_DELETE_WINDOW_atom,1); return window; } void map_raise_window(Window a_window) const { if(!m_display) return; ::XMapWindow(m_display,a_window); ::XRaiseWindow(m_display,a_window); } void show_window(Window a_window) const { if(!m_display) return; XWindowAttributes watbs; ::XGetWindowAttributes(m_display,a_window,&watbs); if(watbs.map_state!=IsUnmapped) return; ::XMapWindow(m_display,a_window); ::XRaiseWindow(m_display,a_window); {XEvent event; ::XIfEvent(m_display,&event,wait_map_notify,(char*)a_window);} } void hide_window(Window a_window) const { if(!m_display) return; XWindowAttributes watbs; ::XGetWindowAttributes(m_display,a_window,&watbs); if(watbs.map_state==IsUnmapped) return; ::XUnmapWindow(m_display,a_window); {XEvent event; ::XIfEvent(m_display,&event,wait_unmap_notify,(char*)a_window);} } void resize_window(Window a_window,unsigned int a_width,unsigned int a_height) const { if(!m_display) return; unsigned int mask = CWWidth | CWHeight | CWBorderWidth; XWindowChanges changes; changes.border_width = 0; changes.width = a_width; changes.height = a_height; ::XConfigureWindow(m_display,a_window,mask,&changes); } bool window_size(Window a_window,int& a_width,int& a_height) const { if(!m_display) {a_width = 0;a_height = 0;return false;} XWindowAttributes watbs; if(!XGetWindowAttributes(m_display,a_window,&watbs)) {a_width = 0;a_height = 0;return false;} a_width = watbs.width; a_height = watbs.height; return true; } void delete_window(Window a_window) const { if(!m_display) return; ::XDestroyWindow(m_display,a_window); } void set_override_redirect(Window a_window) const { //must be executed before window is mapped. if(!m_display) return; XSetWindowAttributes swa; swa.override_redirect = True; ::XChangeWindowAttributes(m_display,a_window,CWOverrideRedirect,&swa); } void set_wm_no_decorations(Window a_window) const { //must be executed before window is mapped. if(!m_display) return; // struct, mwm_hints_decorations taken from OpenMotif MwmUtils.h. struct{ unsigned long flags; unsigned long functions; unsigned long decorations; long inputMode; unsigned long status; } prop; unsigned long mwm_hints_decorations = 1L << 1; Atom atom = ::XInternAtom(m_display,"_MOTIF_WM_HINTS",False); if(atom==None) { m_out << "toolx::X11::base_session::set_wm_no_decorations : can't create/get _MOTIF_WM_HINTS Atom." << std::endl; return; } prop.flags = mwm_hints_decorations; prop.functions = 0; prop.decorations = 0; ::XChangeProperty(m_display,a_window,atom,atom,32,PropModeReplace,(unsigned char*)&prop,5); } bool post(Window a_win, long a_0 = 0,long a_1 = 0, long a_2 = 0,long a_3 = 0, long a_4 = 0) const { if(!m_display) return false; XEvent event; event.type = ClientMessage; event.xclient.display = m_display; event.xclient.window = a_win; event.xclient.message_type = XA_INTEGER; event.xclient.format = 8; /* put 8 because bug with 32 on VMCMS */ event.xclient.data.l[0] = a_0; event.xclient.data.l[1] = a_1; event.xclient.data.l[2] = a_2; event.xclient.data.l[3] = a_3; event.xclient.data.l[4] = a_4; Status stat = ::XSendEvent(m_display,a_win,False,0L,&event); ::XFlush(m_display); return (stat==0?false:true); } bool post_EXIT_STEER(Window a_win) { return post(a_win,SESSION_EXIT_STEER_atom()); } bool post_expose(Window a_win) const { int width,height; if(!window_size(a_win,width,height)) return false; XEvent event; event.type = Expose; event.xexpose.serial = 0; event.xexpose.send_event = True; event.xexpose.display = m_display; event.xexpose.window = a_win; event.xexpose.x = 0; event.xexpose.y = 0; event.xexpose.width = width; event.xexpose.height = height; event.xexpose.count = 0; //locks Status stat = ::XSendEvent(m_display,a_win,False,0L,&event); ::XFlush(m_display); //unlock(); return (stat==0?false:true); } ////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// void add_dispatcher(dispatcher* a_dispatcher) { m_dispatchers.push_back(a_dispatcher); //take ownership. } void invalidate_dispatchers_with_window(Window a_win){ tools_vforit(dispatcher*,m_dispatchers,it) { if((*it)->window()==a_win) (*it)->invalidate(); } } void remove_dispatchers_with_window(Window a_win){ for(auto it = m_dispatchers.begin(); it != m_dispatchers.end();) { if((*it)->window()==a_win) { dispatcher* obj = *it; it = m_dispatchers.erase(it); delete obj; } else { it++; } } } bool dispatch(XEvent& a_event) { {for(auto it = m_dispatchers.begin(); it != m_dispatchers.end();) { if(!(*it)->is_valid()) { dispatcher* obj = *it; it = m_dispatchers.erase(it); delete obj; } else { it++; } }} tools_vforit(dispatcher*,m_dispatchers,it) { if((*it)->is_valid()) { if((*it)->dispatch(a_event)) return true; } } return false; } protected: static Bool wait_map_notify(Display*,XEvent* a_event,char* a_arg){ return (a_event->type == MapNotify) && (a_event->xmap.window == (Window)a_arg); } static Bool wait_unmap_notify(Display*,XEvent* a_event,char* a_arg){ return (a_event->type == UnmapNotify) && (a_event->xmap.window == (Window)a_arg); } void clear_dispatchers() { for(auto it = m_dispatchers.begin(); it != m_dispatchers.end();) { dispatcher* obj = *it; it = m_dispatchers.erase(it); delete obj; } m_dispatchers.clear(); } protected: std::ostream& m_out; unsigned int m_monitor; Display* m_display; Atom m_WM_DELETE_WINDOW_atom; Atom m_SESSION_EXIT_STEER_atom; std::vector m_dispatchers; }; }} #endif