// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_X11_base_session #define tools_X11_base_session // pure X11 code, no GL. #include #include #include "dispatcher" #include #include //XA_INTEGER #include //XVisualInfo namespace tools { namespace X11 { class base_session { public: //virtual bool make_current(Window) const { // if(!m_display) return false; // return true; //} //virtual bool swap_buffers(Window) const { // if(!m_display) return false; // return true; //} 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) { if(!initialize()) {} //throw } virtual ~base_session() { clear_dispatchers(); finalize(); //std::cout << "debug : ~base_session" << std::endl; } public: 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) { copy_dispatchers(a_from.m_dispatchers); if(!initialize()) {} //throw } base_session& operator=(const base_session& a_from){ if(&a_from==this) return *this; m_monitor = a_from.m_monitor; clear_dispatchers(); finalize(); copy_dispatchers(a_from.m_dispatchers); if(!initialize()) {} //throw return *this; } protected: bool initialize() { if(m_display) return true; //done. //NOTE : macOS : XOpenDisplay leaks 128 bytes. m_display = ::XOpenDisplay(NULL); if(!m_display) { m_out << "tools::X11::base_session::initialize : can't open display." << std::endl; return false; } int monitors = ::XScreenCount(m_display); if(int(m_monitor)>=monitors) { m_out << "tools::X11::base_session::initialize : bad monitor index " << m_monitor << ". (#monitors " << monitors << ")." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return false; } m_WM_DELETE_WINDOW_atom = ::XInternAtom(m_display,"WM_DELETE_WINDOW",False); if(m_WM_DELETE_WINDOW_atom==None) { m_out << "tools::X11::base_session::initialize : can't create/get WM_DELETE_WINDOW Atom." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return false; } m_SESSION_EXIT_STEER_atom = ::XInternAtom(m_display,"TOOLS_X11_SESSION_EXIT_STEER",False); if(m_SESSION_EXIT_STEER_atom==None) { m_out << "tools::X11::base_session::initialize : can't create/get TOOLS_X11_SESSION_EXIT_STEER Atom." << std::endl; ::XCloseDisplay(m_display); m_display = 0; return false; } return true; } void finalize() { if(!m_display) return; ::XCloseDisplay(m_display); m_display = 0; } 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;} 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; //::glXWaitX(); ::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 << "tools::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; // we should test if already mapped ! Else we are going to forever wait in the below XIfEvent. ::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; ::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_functions = 1L << 0; unsigned long mwm_hints_decorations = 1L << 1; Atom atom = ::XInternAtom(m_display,"_MOTIF_WM_HINTS",False); if(atom==None) { m_out << "tools::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; //lock(); Status stat = ::XSendEvent(m_display,a_win,False,0L,&event); ::XFlush(m_display); //unlock(); return (stat==0?false:true); } bool post_EXIT_STEER(Window a_win) { return post(a_win,SESSION_EXIT_STEER_atom()); } ////////////////////////////////////////////////// ////////////////////////////////////////////////// ////////////////////////////////////////////////// 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){ tools_vforit_npp(dispatcher*,m_dispatchers,it) { if((*it)->window()==a_win) { dispatcher* obj = *it; it = m_dispatchers.erase(it); delete obj; } else { it++; } } } bool dispatch(XEvent& a_event) { {tools_vforit_npp(dispatcher*,m_dispatchers,it) { 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 wait_xxx(Window a_window) { if(!m_display) return; {wait_what arg; arg.m_event_type = ConfigureNotify; arg.m_window = a_window; XEvent event; ::XIfEvent(m_display,&event,wait_for,(char*)&arg); dispatch(event);} } struct wait_what { int m_event_type; Window m_window; }; static Bool wait_for(Display*,XEvent* a_event,char* a_arg){ wait_what* arg = (wait_what*)a_arg; return (a_event->type == arg->m_event_type) && (a_event->xmap.window == arg->m_window); } */ void clear_dispatchers() { tools_vforit_npp(dispatcher*,m_dispatchers,it) { dispatcher* obj = *it; it = m_dispatchers.erase(it); delete obj; } m_dispatchers.clear(); } void copy_dispatchers(const std::vector& a_from) { clear_dispatchers(); tools_vforcit(dispatcher*,a_from,it) { m_dispatchers.push_back((*it)->copy()); } } 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