diff --git a/src/ui/manager.cpp b/src/ui/manager.cpp index 314239dd7..fdb8d8226 100644 --- a/src/ui/manager.cpp +++ b/src/ui/manager.cpp @@ -47,8 +47,7 @@ namespace ui { static const int NFILTERS = (int)(kFirstRegisteredMessage+1); -struct Filter -{ +struct Filter { int message; Widget* widget; @@ -67,6 +66,7 @@ static WidgetsList new_windows; // Windows that we should show static WidgetsList mouse_widgets_list; // List of widgets to send mouse events static Messages msg_queue; // Messages queue static Filters msg_filters[NFILTERS]; // Filters for every enqueued message +static int filter_locks = 0; static Widget* focus_widget; // The widget with the focus static Widget* mouse_widget; // The widget with the mouse @@ -83,6 +83,37 @@ static int cmp_right(Widget* widget, int x, int y); static int cmp_up(Widget* widget, int x, int y); static int cmp_down(Widget* widget, int x, int y); +namespace { + +class LockFilters { +public: + LockFilters() { + ++filter_locks; + } + ~LockFilters() { + ASSERT(filter_locks > 0); + --filter_locks; + + if (filter_locks == 0) { + // Clear empty filters + for (Filters& msg_filter : msg_filters) { + for (auto it = msg_filter.begin(); it != msg_filter.end(); ) { + Filter* filter = *it; + if (filter->widget == nullptr) { + delete filter; + it = msg_filter.erase(it); + } + else { + ++it; + } + } + } + } + } +}; + +} // anonymous namespace + Manager::Manager() : Widget(kManagerWidget) , m_display(NULL) @@ -127,15 +158,13 @@ Manager::~Manager() Timer::checkNoTimers(); // Destroy filters - for (int c=0; ctype() == kKeyDownMessage || - msg->type() == kKeyUpMessage) { - int mods = (int)static_cast(msg)->modifiers(); - TRACE("Key%s scancode=%d unicode=%d mods=%s%s%s\n", - (msg->type() == kKeyDownMessage ? "Down": "Up"), - static_cast(msg)->scancode(), - static_cast(msg)->unicodeChar(), - mods & kKeyShiftModifier ? " Shift": "", - mods & kKeyCtrlModifier ? " Ctrl": "", - mods & kKeyAltModifier ? " Alt": ""); - } -#endif - - // Check if this message must be filtered by some widget before - int c = msg->type(); - if (c >= kFirstRegisteredMessage) - c = kFirstRegisteredMessage; - - if (!msg_filters[c].empty()) { // OK, so are filters to add... - // Add all the filters in the destination list of the message - for (Filters::reverse_iterator it=msg_filters[c].rbegin(), - end=msg_filters[c].rend(); it != end; ++it) { - Filter* filter = *it; - if (msg->type() == filter->message) - msg->prependRecipient(filter->widget); - } - } - - if (msg->hasRecipients()) - msg_queue.push_back(msg); - else - delete msg; + ASSERT(msg); + msg_queue.push_back(msg); } Window* Manager::getTopWindow() @@ -931,6 +922,7 @@ void Manager::removeMessagesForTimer(Timer* timer) void Manager::addMessageFilter(int message, Widget* widget) { + LockFilters lock; int c = message; if (c >= kFirstRegisteredMessage) c = kFirstRegisteredMessage; @@ -940,32 +932,25 @@ void Manager::addMessageFilter(int message, Widget* widget) void Manager::removeMessageFilter(int message, Widget* widget) { + LockFilters lock; int c = message; if (c >= kFirstRegisteredMessage) c = kFirstRegisteredMessage; - for (Filters::iterator it=msg_filters[c].begin(); it != msg_filters[c].end(); ) { - Filter* filter = *it; - if (filter->widget == widget) { - delete filter; - it = msg_filters[c].erase(it); - } - else - ++it; + Filters& msg_filter = msg_filters[c]; + for (Filter* filter : msg_filter) { + if (filter->widget == widget) + filter->widget = nullptr; } } void Manager::removeMessageFilterFor(Widget* widget) { - for (int c=0; cwidget == widget) { - delete filter; - it = msg_filters[c].erase(it); - } - else - ++it; + LockFilters lock; + for (Filters& msg_filter : msg_filters) { + for (Filter* filter : msg_filter) { + if (filter->widget == widget) + filter->widget = nullptr; } } } @@ -1235,7 +1220,7 @@ void Manager::pumpQueue() base::tick_t t = base::current_tick(); #endif - Messages::iterator it = msg_queue.begin(); + auto it = msg_queue.begin(); while (it != msg_queue.end()) { #ifdef LIMIT_DISPATCH_TIME if (base::current_tick()-t > 250) @@ -1262,102 +1247,36 @@ void Manager::pumpQueue() } bool done = false; - for (auto widget : msg->recipients()) { - if (!widget) - continue; -#ifdef REPORT_EVENTS - { - static const char* msg_name[] = { - "kOpenMessage", - "kCloseMessage", - "kCloseDisplayMessage", - "kResizeDisplayMessage", - "kPaintMessage", - "kTimerMessage", - "kDropFilesMessage", - "kWinMoveMessage", + // Send this message to filters + { + Filters& msg_filter = msg_filters[MIN(msg->type(), kFirstRegisteredMessage)]; + if (!msg_filter.empty()) { + LockFilters lock; + for (Filter* filter : msg_filter) { + // The widget can be nullptr in case that the filter was + // "pre-removed" (it'll finally erased from the + // msg_filter list from ~LockFilters()). + if (filter->widget != nullptr && + msg->type() == filter->message) { + msg->setFromFilter(true); + done = sendMessageToWidget(msg, filter->widget); + msg->setFromFilter(false); - "kKeyDownMessage", - "kKeyUpMessage", - "kFocusEnterMessage", - "kFocusLeaveMessage", - - "kMouseDownMessage", - "kMouseUpMessage", - "kDoubleClickMessage", - "kMouseEnterMessage", - "kMouseLeaveMessage", - "kMouseMoveMessage", - "kSetCursorMessage", - "kMouseWheelMessage", - "kTouchMagnifyMessage", - }; - const char* string = - (msg->type() >= kOpenMessage && - msg->type() <= kMouseWheelMessage) ? msg_name[msg->type()]: - "Unknown"; - - std::cout << "Event " << msg->type() << " (" << string << ") " - << "for " << typeid(*widget).name(); - if (!widget->id().empty()) - std::cout << " (" << widget->id() << ")"; - std::cout << std::endl; - } -#endif - - // We need to configure the clip region for paint messages - // before we call Widget::sendMessage(). - if (msg->type() == kPaintMessage) { - if (widget->hasFlags(HIDDEN)) - continue; - - PaintMessage* paintMsg = static_cast(msg); - she::Surface* surface = m_display->getSurface(); - gfx::Rect oldClip = surface->getClipBounds(); - - if (surface->intersectClipRect(paintMsg->rect())) { -#ifdef REPORT_EVENTS - std::cout << " - clip(" - << paintMsg->rect().x << ", " - << paintMsg->rect().y << ", " - << paintMsg->rect().w << ", " - << paintMsg->rect().h << ")" - << std::endl; -#endif - -#ifdef DEBUG_PAINT_EVENTS - { - she::SurfaceLock lock(surface); - surface->fillRect(gfx::rgba(0, 0, 255), paintMsg->rect()); - } - - if (m_display) - m_display->flip(gfx::Rect(0, 0, display_w(), display_h())); - - base::this_thread::sleep_for(0.002); -#endif - - if (surface) { - // Call the message handler - done = widget->sendMessage(msg); - - // Restore clip region for paint messages. - surface->setClipBounds(oldClip); + if (done) + break; } } - - // As this kPaintMessage's rectangle was updated, we can - // remove it from "m_invalidRegion". - m_invalidRegion -= gfx::Region(paintMsg->rect()); - } - else { - // Call the message handler - done = widget->sendMessage(msg); } + } - if (done) - break; + if (!done) { + // Then send the message to its recipients + for (Widget* widget : msg->recipients()) { + done = sendMessageToWidget(msg, widget); + if (done) + break; + } } // Remove the message from the msg_queue @@ -1368,6 +1287,106 @@ void Manager::pumpQueue() } } +bool Manager::sendMessageToWidget(Message* msg, Widget* widget) +{ + if (!widget) + return false; + +#ifdef REPORT_EVENTS + { + static const char* msg_name[] = { + "kOpenMessage", + "kCloseMessage", + "kCloseDisplayMessage", + "kResizeDisplayMessage", + "kPaintMessage", + "kTimerMessage", + "kDropFilesMessage", + "kWinMoveMessage", + + "kKeyDownMessage", + "kKeyUpMessage", + "kFocusEnterMessage", + "kFocusLeaveMessage", + + "kMouseDownMessage", + "kMouseUpMessage", + "kDoubleClickMessage", + "kMouseEnterMessage", + "kMouseLeaveMessage", + "kMouseMoveMessage", + "kSetCursorMessage", + "kMouseWheelMessage", + "kTouchMagnifyMessage", + }; + const char* string = + (msg->type() >= kOpenMessage && + msg->type() <= kMouseWheelMessage) ? msg_name[msg->type()]: + "Unknown"; + + std::cout << "Event " << msg->type() << " (" << string << ") " + << "for " << typeid(*widget).name(); + if (!widget->id().empty()) + std::cout << " (" << widget->id() << ")"; + std::cout << std::endl; + } +#endif + + bool used = false; + + // We need to configure the clip region for paint messages + // before we call Widget::sendMessage(). + if (msg->type() == kPaintMessage) { + if (widget->hasFlags(HIDDEN)) + return false; + + PaintMessage* paintMsg = static_cast(msg); + she::Surface* surface = m_display->getSurface(); + gfx::Rect oldClip = surface->getClipBounds(); + + if (surface->intersectClipRect(paintMsg->rect())) { +#ifdef REPORT_EVENTS + std::cout << " - clip(" + << paintMsg->rect().x << ", " + << paintMsg->rect().y << ", " + << paintMsg->rect().w << ", " + << paintMsg->rect().h << ")" + << std::endl; +#endif + +#ifdef DEBUG_PAINT_EVENTS + { + she::SurfaceLock lock(surface); + surface->fillRect(gfx::rgba(0, 0, 255), paintMsg->rect()); + } + + if (m_display) + m_display->flip(gfx::Rect(0, 0, display_w(), display_h())); + + base::this_thread::sleep_for(0.002); +#endif + + if (surface) { + // Call the message handler + used = widget->sendMessage(msg); + + // Restore clip region for paint messages. + surface->setClipBounds(oldClip); + } + } + + // As this kPaintMessage's rectangle was updated, we can + // remove it from "m_invalidRegion". + m_invalidRegion -= gfx::Region(paintMsg->rect()); + } + else { + // Call the message handler + used = widget->sendMessage(msg); + } + + return used; +} + void Manager::invalidateDisplayRegion(const gfx::Region& region) { // TODO intersect with getDrawableRegion()??? diff --git a/src/ui/manager.h b/src/ui/manager.h index e9bf61986..6193e696a 100644 --- a/src/ui/manager.h +++ b/src/ui/manager.h @@ -45,11 +45,15 @@ namespace ui { // Refreshes the real display with the UI content. void flipDisplay(); + // Adds the given "msg" message to the queue of messages to be + // dispached. "msg" cannot be used after this function, it'll be + // automatically deleted. + void enqueueMessage(Message* msg); + // Returns true if there are messages in the queue to be - // distpatched through jmanager_dispatch_messages(). + // dispatched through dispatchMessages(). bool generateMessages(); void dispatchMessages(); - void enqueueMessage(Message* msg); void addToGarbage(Widget* widget); void collectGarbage(); @@ -146,6 +150,8 @@ namespace ui { void handleWindowZOrder(); void pumpQueue(); + bool sendMessageToWidget(Message* msg, Widget* widget); + static void removeWidgetFromRecipients(Widget* widget, Message* msg); static bool someParentIsFocusStop(Widget* widget); static Widget* findMagneticWidget(Widget* widget); diff --git a/src/ui/message.cpp b/src/ui/message.cpp index 3e4c9e394..bd5776a6c 100644 --- a/src/ui/message.cpp +++ b/src/ui/message.cpp @@ -22,6 +22,7 @@ namespace ui { Message::Message(MessageType type, KeyModifiers modifiers) : m_type(type) , m_used(false) + , m_fromFilter(false) { if (modifiers == kKeyUninitializedModifier) m_modifiers = she::instance()->keyModifiers(); diff --git a/src/ui/message.h b/src/ui/message.h index d78f75166..c2ee8eab6 100644 --- a/src/ui/message.h +++ b/src/ui/message.h @@ -37,6 +37,8 @@ namespace ui { const WidgetsList& recipients() const { return m_recipients; } bool hasRecipients() const { return !m_recipients.empty(); } bool isUsed() const { return m_used; } + bool fromFilter() const { return m_fromFilter; } + void setFromFilter(bool state) { m_fromFilter = state; } void markAsUsed() { m_used = true; } KeyModifiers modifiers() const { return m_modifiers; } bool shiftPressed() const { return (m_modifiers & kKeyShiftModifier) == kKeyShiftModifier; } @@ -57,9 +59,10 @@ namespace ui { void broadcastToChildren(Widget* widget); private: - MessageType m_type; // Type of message + MessageType m_type; // Type of message WidgetsList m_recipients; // List of recipients of the message bool m_used; // Was used + bool m_fromFilter; // Sent from pre-filter KeyModifiers m_modifiers; // Key modifiers pressed when message was created }; diff --git a/src/ui/popup_window.cpp b/src/ui/popup_window.cpp index a49791a86..96bd55a93 100644 --- a/src/ui/popup_window.cpp +++ b/src/ui/popup_window.cpp @@ -116,6 +116,13 @@ bool PopupWindow::onProcessMessage(Message* msg) closeWindow(this); return true; } + + // If the message came from a filter, we don't send it back to + // the default Window processing (which will send the message + // to the Manager). In this way, the focused children can + // process the kKeyDownMessage. + if (msg->fromFilter()) + return false; } break;