X11AppContext::~X11AppContext() { if(xDisplay_) ::XFlush(&xDisplay()); xcb_ewmh_connection_wipe(&ewmhConnection()); impl_.reset(); if(xDummyWindow_) xcb_destroy_window(xConnection_, xDummyWindow_); if(xDisplay_) ::XCloseDisplay(&xDisplay()); xDisplay_ = nullptr; xConnection_ = nullptr; xDefaultScreen_ = nullptr; }
bool GlxContext::swapInterval(int interval, std::error_code& ec) const { //TODO: check for interval < 0 and tear extensions not supported. ec.clear(); if(!GLAD_GLX_EXT_swap_control || !glXSwapIntervalEXT) { ec = {GlContextErrc::extensionNotSupported}; return false; } const GlSurface* currentSurface; GlContext::current(¤tSurface); auto currentGlxSurface = dynamic_cast<const GlxSurface*>(currentSurface); if(!currentGlxSurface) { // ec = {GlContextErrorCode::surfaceNotCurrent}; //TODO: add error for this case return false; } ::glXSwapIntervalEXT(xDisplay(), currentGlxSurface->xDrawable(), interval); //TODO: handle possible error into ec return true; }
GlxContext::~GlxContext() { if(glxContext_) { std::error_code ec; if(!makeNotCurrent(ec)) warning("ny::~GlxContext: failed to make the context not current: ", ec.message()); ::glXDestroyContext(xDisplay(), glxContext_); } }
/*=========================================================================== ===========================================================================*/ void SamplePosDet_Printf( CSamplePosDet *pMe, int nLine, int nCol, AEEFont fnt, uint32 dwFlags, const char *szFormat, ... ) { char szBuf[64]; va_list args; va_start( args, szFormat ); (void)VSNPRINTF( szBuf, 64, szFormat, args ); va_end( args ); xDisplay( (AEEApplet *)pMe, nLine, nCol, fnt, dwFlags, szBuf ); }
bool GlxContext::makeNotCurrentImpl(std::error_code& ec) { ec.clear(); if(!::glXMakeCurrent(xDisplay(), 0, nullptr)) { //TODO: handle error into ec warning("ny::GlxContext::makeNotCurrentImpl (glXMakeCurrent) failed"); return false; } return true; }
bool GlxContext::makeCurrentImpl(const GlSurface& surface, std::error_code& ec) { ec.clear(); auto drawable = dynamic_cast<const GlxSurface*>(&surface)->xDrawable(); if(!::glXMakeCurrent(xDisplay(), drawable, glxContext_)) { //TODO: handle error into ec warning("ny::GlxContext::makeCurrentImpl (glXMakeCurrent) failed"); return false; } return true; }
xcb_atom_t X11AppContext::atom(const std::string& name) { auto it = additionalAtoms_.find(name); if(it == additionalAtoms_.end()) { auto cookie = xcb_intern_atom(xConnection_, 0, name.size(), name.c_str()); xcb_generic_error_t* error; auto reply = xcb_intern_atom_reply(xConnection_, cookie, &error); if(error) { auto msg = x11::errorMessage(xDisplay(), error->error_code); warning("ny::X11AppContext::atom: failed to retrieve ", name, ": ", msg); free(error); return 0u; } it = additionalAtoms_.insert({name, reply->atom}).first; free(reply); } return it->second; }
void X11AppContext::processEvent(const x11::GenericEvent& ev) { //macro for easier event creation for registered EventHandler #define EventHandlerEvent(T, W) \ auto handler = eventHandler(W); \ if(!handler) return; \ auto event = T(handler); auto dispatch = [&](Event& event){ if(event.handler) event.handler->handleEvent(event); }; auto responseType = ev.response_type & ~0x80; switch(responseType) { case XCB_MOTION_NOTIFY: { auto& motion = reinterpret_cast<const xcb_motion_notify_event_t&>(ev); auto pos = nytl::Vec2i(motion.event_x, motion.event_y); mouseContext_->move(pos); EventHandlerEvent(MouseMoveEvent, motion.event); event.position = pos; event.screenPosition = nytl::Vec2i(motion.root_x, motion.root_y); dispatch(event); break; } case XCB_EXPOSE: { auto& expose = reinterpret_cast<const xcb_expose_event_t&>(ev); if(expose.count == 0) { EventHandlerEvent(DrawEvent, expose.window); dispatch(event); } break; } case XCB_MAP_NOTIFY: { auto& map = reinterpret_cast<const xcb_map_notify_event_t&>(ev); EventHandlerEvent(DrawEvent, map.window); dispatch(event); break; } case XCB_BUTTON_PRESS: { auto& button = reinterpret_cast<const xcb_button_press_event_t&>(ev); int scroll = 0; if(button.detail == 4) scroll = 1; else if(button.detail == 5) scroll = -1; if(scroll) { EventHandlerEvent(MouseWheelEvent, button.event); event.data = std::make_unique<X11EventData>(ev); event.value = scroll; mouseContext_->onWheel(*mouseContext_, scroll); dispatch(event); break; } auto b = x11ToButton(button.detail); mouseContext_->mouseButton(b, true); EventHandlerEvent(MouseButtonEvent, button.event); event.data = std::make_unique<X11EventData>(ev); event.button = b; event.position = nytl::Vec2i(button.event_x, button.event_y); event.pressed = true; dispatch(event); break; } case XCB_BUTTON_RELEASE: { auto& button = reinterpret_cast<const xcb_button_release_event_t&>(ev); if(button.detail == 4 || button.detail == 5) break; auto b = x11ToButton(button.detail); mouseContext_->mouseButton(b, false); EventHandlerEvent(MouseButtonEvent, button.event); event.data = std::make_unique<X11EventData>(ev); event.button = b; event.position = nytl::Vec2i(button.event_x, button.event_y); event.pressed = false; dispatch(event); break; } case XCB_ENTER_NOTIFY: { auto& enter = reinterpret_cast<const xcb_enter_notify_event_t&>(ev); auto wc = windowContext(enter.event); mouseContext_->over(wc); EventHandlerEvent(MouseCrossEvent, enter.event); event.position = nytl::Vec2i(enter.event_x, enter.event_y); event.entered = true; dispatch(event); break; } case XCB_LEAVE_NOTIFY: { auto& leave = reinterpret_cast<const xcb_enter_notify_event_t&>(ev); auto wc = windowContext(leave.event); if(mouseContext_->over() == wc) mouseContext_->over(nullptr); EventHandlerEvent(MouseCrossEvent, leave.event); event.position = nytl::Vec2i(leave.event_x, leave.event_y); event.entered = false; dispatch(event); break; } case XCB_FOCUS_IN: { auto& focus = reinterpret_cast<const xcb_focus_in_event_t&>(ev); auto wc = windowContext(focus.event); keyboardContext_->focus(wc); EventHandlerEvent(FocusEvent, focus.event); event.focus = true; dispatch(event); break; } case XCB_FOCUS_OUT: { auto& focus = reinterpret_cast<const xcb_focus_in_event_t&>(ev); auto wc = windowContext(focus.event); if(keyboardContext_->focus() == wc)keyboardContext_->focus(nullptr); EventHandlerEvent(FocusEvent, focus.event); event.focus = false; dispatch(event); break; } case XCB_KEY_PRESS: { auto& key = reinterpret_cast<const xcb_key_press_event_t&>(ev); EventHandlerEvent(KeyEvent, key.event); event.pressed = true; if(!keyboardContext_->keyEvent(key.detail, event)) bell(); dispatch(event); break; } case XCB_KEY_RELEASE: { auto& key = reinterpret_cast<const xcb_key_press_event_t&>(ev); EventHandlerEvent(KeyEvent, key.event); event.pressed = false; if(!keyboardContext_->keyEvent(key.detail, event)) bell(); dispatch(event); break; } case XCB_REPARENT_NOTIFY: { auto& reparent = reinterpret_cast<const xcb_reparent_notify_event_t&>(ev); auto wc = windowContext(reparent.window); if(wc) wc->reparentEvent(); break; } case XCB_CONFIGURE_NOTIFY: { auto& configure = reinterpret_cast<const xcb_configure_notify_event_t&>(ev); //todo: something about window state auto nsize = nytl::Vec2ui(configure.width, configure.height); // auto npos = nytl::Vec2i(configure.x, configure.y); //positionEvent auto wc = windowContext(configure.window); if(wc) wc->sizeEvent(nsize); if(!eventHandler(configure.window)) break; /* TODO XXX !important if(any(windowContext(configure.window)->window().size() != nsize)) //sizeEvent { EventHandlerEvent(SizeEvent, configure.window); event->size = nsize; event->change = 0; dispatcher.dispatch(std::move(event)); auto wc = windowContext(configure.window); if(!wc) return true; auto wevent = std::make_unique<SizeEvent>(wc); wevent->size = nsize; wevent->change = 0; dispatcher.dispatch(std::move(wevent)); } if(any(windowContext(configure.window)->window().position() != npos)) { EventHandlerEvent(PositionEvent, configure.window); event->position = npos; event->change = 0; dispatcher.dispatch(std::move(event)); } */ break; } case XCB_CLIENT_MESSAGE: { auto& client = reinterpret_cast<const xcb_client_message_event_t&>(ev); auto protocol = static_cast<unsigned int>(client.data.data32[0]); if(protocol == atoms().wmDeleteWindow) { EventHandlerEvent(CloseEvent, client.window); dispatch(event); } break; } case 0u: { //an error occurred! int code = reinterpret_cast<const xcb_generic_error_t&>(ev).error_code; auto errorMsg = x11::errorMessage(xDisplay(), code); warning("ny::X11AppContext::processEvent: retrieved error code ", code, ", ", errorMsg); break; } default: { //check for xkb event if(ev.response_type == keyboardContext_->xkbEventType()) keyboardContext_->processXkbEvent(ev); // May be needed for gl to work correctly... (TODO: test) // XLockDisplay(xDisplay_); // auto proc = XESetWireToEvent(xDisplay_, ev.response_type & ~0x80, nullptr); // if(proc) // { // XESetWireToEvent(xDisplay_, ev.response_type & ~0x80, proc); // XEvent dummy; // ev.sequence = LastKnownRequestProcessed(xDisplay_); // if(proc(xDisplay_, &dummy, (xEvent*) &ev)) //not handled // { // //TODO // } // } // // XUnlockDisplay(xDisplay_); } } #undef EventHandlerEvent }
//appContext X11AppContext::X11AppContext() { //XInitThreads(); //todo, make this optional impl_ = std::make_unique<Impl>(); xDisplay_ = ::XOpenDisplay(nullptr); if(!xDisplay_) throw std::runtime_error("ny::X11AppContext: could not connect to X Server"); xDefaultScreenNumber_ = ::XDefaultScreen(xDisplay_); xConnection_ = ::XGetXCBConnection(xDisplay_); if(!xConnection_ || xcb_connection_has_error(xConnection_)) throw std::runtime_error("ny::X11AppContext: unable to get xcb connection"); impl_->errorCategory = {*xDisplay_, *xConnection_}; auto ewmhCookie = xcb_ewmh_init_atoms(&xConnection(), &ewmhConnection()); //query information auto iter = xcb_setup_roots_iterator(xcb_get_setup(&xConnection())); for(auto i = 0; iter.rem; ++i, xcb_screen_next(&iter)) { if(i == xDefaultScreenNumber_) { xDefaultScreen_ = iter.data; break; } } //This must be called because xcb is used to access the event queue ::XSetEventQueueOwner(xDisplay_, XCBOwnsEventQueue); //Generate an x dummy window that can e.g. be used for selections //This window remains invisible, i.e. it is not begin mapped xDummyWindow_ = xcb_generate_id(xConnection_); auto cookie = xcb_create_window_checked(xConnection_, XCB_COPY_FROM_PARENT, xDummyWindow_, xDefaultScreen_->root, 0, 0, 50, 50, 0, XCB_WINDOW_CLASS_INPUT_ONLY, XCB_COPY_FROM_PARENT, 0, nullptr); errorCategory().checkThrow(cookie, "ny::X11AppContext: create_window for dummy window failed"); //Load all default required atoms auto& atoms = impl_->atoms; struct { xcb_atom_t& atom; const char* name; } atomNames[] = { {atoms.xdndEnter, "XdndEnter"}, {atoms.xdndPosition, "XdndPosition"}, {atoms.xdndStatus, "XdndStatus"}, {atoms.xdndTypeList, "XdndTypeList"}, {atoms.xdndActionCopy, "XdndActionCopy"}, {atoms.xdndActionMove, "XdndActionMove"}, {atoms.xdndActionAsk, "XdndActionAsk"}, {atoms.xdndDrop, "XdndDrop"}, {atoms.xdndLeave, "XdndLeave"}, {atoms.xdndFinished, "XdndFinished"}, {atoms.xdndSelection, "XdndSelection"}, {atoms.xdndProxy, "XdndProxy"}, {atoms.xdndAware, "XdndAware"}, {atoms.clipboard, "CLIPBOARD"}, {atoms.targets, "TARGETS"}, {atoms.text, "TEXT"}, {atoms.utf8string, "UTF8_STRING"}, {atoms.fileName, "FILE_NAME"}, {atoms.wmDeleteWindow, "WM_DELETE_WINDOW"}, {atoms.motifWmHints, "_MOTIF_WM_HINTS"}, {atoms.mime.textPlain, "text/plain"}, {atoms.mime.textPlainUtf8, "text/plain;charset=utf8"}, {atoms.mime.textUriList, "text/uri-list"}, {atoms.mime.imageJpeg, "image/jpeg"}, {atoms.mime.imageGif, "image/gif"}, {atoms.mime.imagePng, "image/png"}, {atoms.mime.imageBmp, "image/bmp"}, {atoms.mime.imageData, "image/x-ny-data"}, {atoms.mime.timePoint, "x-application/ny-time-point"}, {atoms.mime.timeDuration, "x-application/ny-time-duration"}, {atoms.mime.raw, "x-application/ny-raw-buffer"}, }; auto length = sizeof(atomNames) / sizeof(atomNames[0]); std::vector<xcb_intern_atom_cookie_t> atomCookies; atomCookies.reserve(length); for(auto& name : atomNames) atomCookies.push_back(xcb_intern_atom(xConnection_, 0, std::strlen(name.name), name.name)); for(auto i = 0u; i < atomCookies.size(); ++i) { xcb_generic_error_t* error {}; auto reply = xcb_intern_atom_reply(xConnection_, atomCookies[i], &error); if(reply) { atomNames[i].atom = reply->atom; free(reply); continue; } else if(error) { auto msg = x11::errorMessage(xDisplay(), error->error_code); free(error); warning("ny::X11AppContext: Failed to load atom ", atomNames[i].name, ": ", msg); } } //ewmh xcb_ewmh_init_atoms_replies(&ewmhConnection(), ewmhCookie, nullptr); //input keyboardContext_ = std::make_unique<X11KeyboardContext>(*this); mouseContext_ = std::make_unique<X11MouseContext>(*this); }
GlxContext::GlxContext(const GlxSetup& setup, const GlContextSettings& settings) : setup_(&setup) { auto major = settings.version.major; auto minor = settings.version.minor; if(major == 0 && minor == 0) { major = 4; minor = 5; } //test for logical errors if(settings.version.api != GlApi::gl) throw GlContextError(GlContextErrc::invalidApi, "ny::WglContext"); if(major < 1 || major > 4 || minor > 5) throw GlContextError(GlContextErrc::invalidVersion, "ny::GlxContext"); //config GlConfig glConfig; GLXFBConfig glxConfig; if(settings.config) { glConfig = setup.config(settings.config); glxConfig = setup.glxConfig(settings.config); } else { glConfig = setup.defaultConfig(); glxConfig = setup.glxConfig(glConfig.id); } if(!glxConfig) throw GlContextError(GlContextErrc::invalidConfig, "ny::GlxContext"); //shared GLXContext glxShareContext = nullptr; if(settings.share) { auto shareCtx = dynamic_cast<GlxContext*>(settings.share); if(!shareCtx) throw GlContextError(GlContextErrc::invalidSharedContext, "ny::EglContext"); glxShareContext = shareCtx->glxContext(); } //set a new error handler auto oldErrorHandler = ::XSetErrorHandler(&ctxErrorHandler); if(GLAD_GLX_ARB_create_context && glXCreateContextAttribsARB) { std::vector<int> contextAttribs; //profile contextAttribs.push_back(GLX_CONTEXT_PROFILE_MASK_ARB); if(!settings.compatibility) contextAttribs.push_back(GLX_CONTEXT_CORE_PROFILE_BIT_ARB); else contextAttribs.push_back(GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB); //forward compatible, debug auto flags = 0u; if(settings.forwardCompatible) flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; if(settings.debug) flags |= GLX_CONTEXT_DEBUG_BIT_ARB; contextAttribs.push_back(GLX_CONTEXT_FLAGS_ARB); contextAttribs.push_back(flags); //version contextAttribs.push_back(GLX_CONTEXT_MAJOR_VERSION_ARB); contextAttribs.push_back(major); contextAttribs.push_back(GLX_CONTEXT_MINOR_VERSION_ARB); contextAttribs.push_back(minor); //end contextAttribs.push_back(0); glxContext_ = ::glXCreateContextAttribsARB(xDisplay(), glxConfig, glxShareContext, true, contextAttribs.data()); if(!glxContext_ && !settings.forceVersion) { //those versions will be tried to create when the version specified in //the passed settings fails and the passed version should not be forced. constexpr std::pair<unsigned int, unsigned int> versionPairs[] = {{4, 5}, {3, 3}, {3, 2}, {3, 1}, {3, 0}, {1, 2}, {1, 0}}; for(const auto& p : versionPairs) { contextAttribs[contextAttribs.size() - 4] = p.first; contextAttribs[contextAttribs.size() - 2] = p.second; glxContext_ = glXCreateContextAttribsARB(xDisplay(), glxConfig, glxShareContext, true, contextAttribs.data()); if(glxContext_) break; } } } if(!glxContext_ && !settings.forceVersion) { warning("ny::GlxContext: failed to create modern context, trying legacy method"); glxContext_ = ::glXCreateNewContext(xDisplay(), glxConfig, GLX_RGBA_TYPE, glxShareContext, true); } if(!glxContext_ && !settings.forceVersion) { glxContext_ = ::glXCreateNewContext(xDisplay(), glxConfig, GLX_RGBA_TYPE, glxShareContext, false); } ::XSync(xDisplay(), false); ::XSetErrorHandler(oldErrorHandler); if(!glxContext_) throw std::runtime_error("ny::GlxContext: failed to create glx Context in any way."); if(!::glXIsDirect(xDisplay(), glxContext_)) warning("ny::GlxContext: could only create indirect gl context -> worse performance"); GlContext::initContext(GlApi::gl, glConfig, settings.share); }