void X11BufferSurface::apply(const BufferGuard&) noexcept { if(!active_) { warning("ny::X11BufferSurface::apply: no currently active BufferGuard"); return; } active_ = false; //we use the checked versions here since those function are very error prone due to //the rather complex depth/visual/bpp x system. We catch invalid x request here //directly. auto depth = windowContext().visualDepth(); auto window = windowContext().xWindow(); if(shm_) { auto cookie = xcb_shm_put_image_checked(&xConnection(), window, gc_, size_.x, size_.y, 0, 0, size_.x, size_.y, 0, 0, depth, XCB_IMAGE_FORMAT_Z_PIXMAP, 0, shmseg_, 0); windowContext().errorCategory().checkWarn(cookie, "ny::X11BufferSurface: shm_put_image"); } else { auto bpp = imageDataFormatSize(format_); //Bytes per pixel (XXX NOT bits!) auto length = size_.x * size_.y * bpp; auto cookie = xcb_put_image_checked(&xConnection(), XCB_IMAGE_FORMAT_Z_PIXMAP, window, gc_, size_.x, size_.y, 0, 0, 0, depth, length, data_); windowContext().errorCategory().checkWarn(cookie, "ny::X11BufferSurface: put_image"); } }
BufferGuard WaylandBufferSurface::buffer() { if(active_) throw std::logic_error("ny::WlBufferSurface: there is already an active BufferGuard"); auto size = windowContext().size(); for(auto& b : buffers_) { if(b.used()) continue; if(!nytl::allEqual(b.size(), size)) b.size(size); b.use(); active_ = &b; auto format = waylandToImageFormat(b.format()); return {*this, {&b.data(), size, format, b.stride()}}; } //create new buffer if none is unused buffers_.emplace_back(windowContext().appContext(), size); buffers_.back().use(); active_ = &buffers_.back(); auto format = waylandToImageFormat(buffers_.back().format()); if(format == ImageDataFormat::none) throw std::runtime_error("ny::WaylandBufferSurface: failed to parse shm buffer format"); return {*this, {&buffers_.back().data(), size, format, buffers_.back().stride()}}; }
void WaylandBufferSurface::apply(const BufferGuard& buffer) noexcept { if(!active_ || buffer.get().data != &active_->data()) { warning("ny::WaylandBufferSurface::apply: invalid BufferGuard given"); return; } if(windowContext().shown()) windowContext().attachCommit(&active_->wlBuffer()); active_ = nullptr; }
void Icon::paint(GraphicsContext* context, const IntRect& r) { if (context->paintingDisabled()) return; #if OS(WINCE) #if !USE(CAIRO) context->drawIcon(m_hIcon, r, DI_NORMAL); #else IntRect translatedRect = context->getCTM().mapRect(r); LocalWindowsContext windowContext(context, translatedRect); DrawIconEx(windowContext.hdc(), translatedRect.x(), translatedRect.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); #endif #else LocalWindowsContext windowContext(context, r); DrawIconEx(windowContext.hdc(), r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); #endif }
X11BufferSurface::X11BufferSurface(X11WindowContext& wc) : windowContext_(&wc) { gc_ = xcb_generate_id(&xConnection()); std::uint32_t value[] = {0, 0}; auto c = xcb_create_gc_checked(&xConnection(), gc_, wc.xWindow(), XCB_GC_FOREGROUND, value); windowContext().errorCategory().checkThrow(c, "ny::X11BufferSurface: create_gc"); //query the format //this is needed because the xserver may need a different bpp for an image //with the depth of the window. //For 24-bit depth images the xserver often required 32 bpp. auto setup = xcb_get_setup(&xConnection()); auto fmtit = xcb_setup_pixmap_formats(setup); auto fmtend = fmtit + xcb_setup_pixmap_formats_length(setup); xcb_format_t* fmt {}; while(fmtit != fmtend) { if(fmtit->depth == windowContext().visualDepth()) { fmt = fmtit; break; } ++fmtit; } if(!fmt) throw std::runtime_error("ny::X11BufferSurface: couldn't query depth format bpp"); format_ = visualToFormat(*windowContext().xVisualType(), fmt->bits_per_pixel); if(format_ == ImageDataFormat::none) throw std::runtime_error("ny::X11BufferSurface: couldn't parse visual format"); //check if the server has shm suport //it is also implemented without shm but the performance might be worse auto cookie = xcb_shm_query_version(&xConnection()); auto reply = xcb_shm_query_version_reply(&xConnection(), cookie, nullptr); shm_ = reply; if(reply) free(reply); if(!shm_) warning("ny::X11BufferSurface: shm server does not support shm extension"); }
void Icon::paint(GraphicsContext* context, const IntRect& r) { if (context->paintingDisabled()) return; #if OS(WINCE) context->drawIcon(m_hIcon, r, DI_NORMAL); #else LocalWindowsContext windowContext(context, r); DrawIconEx(windowContext.hdc(), r.x(), r.y(), m_hIcon, r.width(), r.height(), 0, 0, DI_NORMAL); #endif }
void WinapiBufferSurface::apply(const BufferGuard& bufferGuard) noexcept { if(!active_ || bufferGuard.get().data != data_.get()) { warning("ny::WinapiBufferSurface::apply: invalid bufferGuard"); return; } active_ = false; auto bitmap = ::CreateBitmap(size_.x, size_.y, 1, 32, data_.get()); auto whdc = ::GetDC(windowContext().handle()); auto bhdc = ::CreateCompatibleDC(whdc); auto prev = ::SelectObject(bhdc, bitmap); ::BitBlt(whdc, 0, 0, size_.x, size_.y, bhdc, 0, 0, SRCCOPY); ::SelectObject(bhdc, prev); ::DeleteDC(bhdc); ::ReleaseDC(windowContext().handle(), whdc); ::DeleteObject(bitmap); }
BufferGuard X11BufferSurface::buffer() { if(active_) throw std::logic_error("ny::X11BufferSurface::buffer: there is already a BufferGuard"); //check if resize is needed auto size = windowContext().size(); auto bpp = imageDataFormatSize(format_); //bytes per pixel (XXX NOT bits!) auto newBytes = size.x * size.y * bpp; //the needed size if(newBytes > byteSize_) { //we alloc more than is really needed because this will //speed up (especially the shm version) resizes. We don't have to reallocated //every time the window is resized and redrawn for the cost of higher memory //consumption byteSize_ = newBytes * 4; if(shm_) { if(shmseg_) { xcb_shm_detach(&xConnection(), shmseg_); shmdt(data_); shmctl(shmid_, IPC_RMID, 0); } else { shmseg_ = xcb_generate_id(&xConnection()); } shmid_ = shmget(IPC_PRIVATE, byteSize_, IPC_CREAT | 0777); data_ = static_cast<uint8_t*>(shmat(shmid_, 0, 0)); shmseg_ = xcb_generate_id(&xConnection()); xcb_shm_attach(&xConnection(), shmseg_, shmid_, 0); } else { ownedBuffer_ = std::make_unique<uint8_t[]>(byteSize_); data_ = ownedBuffer_.get(); } } size_ = size; active_ = true; return {*this, {data_, {size_.x, size_.y}, format_, size_.x * bpp}}; }
BufferGuard WinapiBufferSurface::buffer() { if(active_) throw std::logic_error("ny::WinapiBufferSurface::get: has already an active BufferGuard"); auto currSize = windowContext().clientExtents().size; auto currTotal = nytl::multiply(currSize); //TODO: allocate more than needed? store really allocated size if(currTotal > nytl::multiply(size_)) data_ = std::make_unique<std::uint8_t[]>(currTotal * 4); size_ = currSize; active_ = true; return {*this, {data_.get(), size_, ImageDataFormat::bgra8888, size_.x * 4}}; }
bool EventDispatcher::dispatchEvent(PassRefPtr<Event> event) { event->setTarget(eventTargetRespectingSVGTargetRules(m_node.get())); ASSERT(!eventDispatchForbidden()); ASSERT(event->target()); ASSERT(!event->type().isNull()); // JavaScript code can create an event with an empty name, but not null. RefPtr<EventTarget> originalTarget = event->target(); ensureEventAncestors(event.get()); WindowEventContext windowContext(event.get(), m_node.get(), topEventContext()); InspectorInstrumentationCookie cookie = InspectorInstrumentation::willDispatchEvent(m_node->document(), *event, windowContext.window(), m_node.get(), m_ancestors); // Give the target node a chance to do some work before DOM event handlers get a crack. void* data = m_node->preDispatchEventHandler(event.get()); if (m_ancestors.isEmpty() || m_shouldPreventDispatch || event->propagationStopped()) goto doneDispatching; // Trigger capturing event handlers, starting at the top and working our way down. event->setEventPhase(Event::CAPTURING_PHASE); if (windowContext.handleLocalEvents(event.get()) && event->propagationStopped()) goto doneDispatching; for (size_t i = m_ancestors.size() - 1; i > 0; --i) { const EventContext& eventContext = m_ancestors[i]; if (eventContext.currentTargetSameAsTarget()) { if (event->bubbles()) continue; event->setEventPhase(Event::AT_TARGET); } else event->setEventPhase(Event::CAPTURING_PHASE); eventContext.handleLocalEvents(event.get()); if (event->propagationStopped()) goto doneDispatching; } event->setEventPhase(Event::AT_TARGET); event->setTarget(originalTarget.get()); event->setCurrentTarget(eventTargetRespectingSVGTargetRules(m_node.get())); m_ancestors[0].handleLocalEvents(event.get()); if (event->propagationStopped()) goto doneDispatching; if (event->bubbles() && !event->cancelBubble()) { // Trigger bubbling event handlers, starting at the bottom and working our way up. event->setEventPhase(Event::BUBBLING_PHASE); size_t size = m_ancestors.size(); for (size_t i = 1; i < size; ++i) { const EventContext& eventContext = m_ancestors[i]; if (eventContext.currentTargetSameAsTarget()) event->setEventPhase(Event::AT_TARGET); else event->setEventPhase(Event::BUBBLING_PHASE); eventContext.handleLocalEvents(event.get()); if (event->propagationStopped() || event->cancelBubble()) goto doneDispatching; } windowContext.handleLocalEvents(event.get()); } doneDispatching: event->setTarget(originalTarget.get()); event->setCurrentTarget(0); event->setEventPhase(0); // Pass the data from the preDispatchEventHandler to the postDispatchEventHandler. m_node->postDispatchEventHandler(event.get(), data); // Call default event handlers. While the DOM does have a concept of preventing // default handling, the detail of which handlers are called is an internal // implementation detail and not part of the DOM. if (!event->defaultPrevented() && !event->defaultHandled()) { // Non-bubbling events call only one default event handler, the one for the target. m_node->defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; // For bubbling events, call default event handlers on the same targets in the // same order as the bubbling phase. if (event->bubbles()) { size_t size = m_ancestors.size(); for (size_t i = 1; i < size; ++i) { m_ancestors[i].node()->defaultEventHandler(event.get()); ASSERT(!event->defaultPrevented()); if (event->defaultHandled()) goto doneWithDefault; } } } doneWithDefault: // Ensure that after event dispatch, the event's target object is the // outermost shadow DOM boundary. event->setTarget(windowContext.target()); event->setCurrentTarget(0); InspectorInstrumentation::didDispatchEvent(cookie); return !event->defaultPrevented(); }
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 }
EventHandler* X11AppContext::eventHandler(xcb_window_t w) { auto* wc = windowContext(w); return wc ? wc->eventHandler() : nullptr; }
//wndProc LRESULT WinapiAppContext::eventProc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { auto wc = windowContext(window); WinapiEventData eventData; eventData.windowContext = wc; eventData.window = window; eventData.message = message; eventData.wparam = wparam; eventData.lparam = lparam; LRESULT result = 0; switch(message) { case WM_PAINT: { if(wc) wc->listener().draw(&eventData); result = ::DefWindowProc(window, message, wparam, lparam); //to validate the window break; } case WM_DESTROY: { if(wc) wc->listener().close(&eventData); break; } case WM_SIZE: { nytl::Vec2ui size(LOWORD(lparam), HIWORD(lparam)); if(wc) wc->listener().resize(size, &eventData); break; } case WM_MOVE: { nytl::Vec2i position(LOWORD(lparam), HIWORD(lparam)); if(wc) wc->listener().position(position, &eventData); break; } case WM_SYSCOMMAND: { if(wc) { constexpr auto SC_DRAGMOVE = 0xf012; ToplevelState state; if(wparam == SC_MAXIMIZE) state = ToplevelState::maximized; else if(wparam == SC_MINIMIZE) state = ToplevelState::minimized; else if(wparam == SC_RESTORE) state = ToplevelState::normal; else if(wparam >= SC_SIZE && wparam <= SC_SIZE + 8) { auto currentCursor = ::GetClassLongPtr(wc->handle(), -12); auto edge = winapiToEdges(wparam - SC_SIZE); auto cursor = sizeCursorFromEdge(edge); wc->cursor(cursor); result = ::DefWindowProc(window, message, wparam, lparam); ::SetClassLongPtr(wc->handle(), -12, currentCursor); break; } //TODO: shown parameter? check WS_VISIBLE? wc->listener().state(true, state, &eventData); } result = ::DefWindowProc(window, message, wparam, lparam); break; } case WM_GETMINMAXINFO: { if(wc) { ::MINMAXINFO* mmi = reinterpret_cast<MINMAXINFO*>(lparam); mmi->ptMaxTrackSize.x = wc->maxSize().x; mmi->ptMaxTrackSize.y = wc->maxSize().y; mmi->ptMinTrackSize.x = wc->minSize().x; mmi->ptMinTrackSize.y = wc->minSize().y; } break; } case WM_ERASEBKGND: { //prevent the background erase result = 1; break; } //XXX: needed? case WM_CLIPBOARDUPDATE: { clipboardSequenceNumber_ = ::GetClipboardSequenceNumber(); clipboardOffer_.reset(); IDataObject* obj; ::OleGetClipboard(&obj); if(obj) clipboardOffer_ = std::make_unique<winapi::DataOfferImpl>(*obj); break; } case WM_QUIT: { receivedQuit_ = 1; break; } default: { //process mouse/keyboard events if(keyboardContext_.processEvent(eventData, result)) break; if(mouseContext_.processEvent(eventData, result)) break; result = ::DefWindowProc(window, message, wparam, lparam); break; } } return result; }