summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-31 22:53:37 +0000
committerjcampan@chromium.org <jcampan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-31 22:53:37 +0000
commit148d105e27c7c8c2cda0c81292690b9edafcae1f (patch)
treee278052fc84a6e5399aec5bd0d423162aecb6d16 /base
parentdc75d4823b598a9b9b313728a06f6b47d6a73929 (diff)
downloadchromium_src-148d105e27c7c8c2cda0c81292690b9edafcae1f.zip
chromium_src-148d105e27c7c8c2cda0c81292690b9edafcae1f.tar.gz
chromium_src-148d105e27c7c8c2cda0c81292690b9edafcae1f.tar.bz2
This CL adds accelerators to the Linux toolkit views.
The MessageLoop had to be modified to support Dispatchers on Linux. BUG=None TEST=On Windows and Linux, make sure the accelerators still work as expected. On Linux toolkit views, build and run the unit-tests. Review URL: http://codereview.chromium.org/159046 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@22210 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/keyboard_codes.h6
-rw-r--r--base/keyboard_codes_linux.h214
-rw-r--r--base/keyboard_codes_mac.h (renamed from base/keyboard_codes_posix.h)6
-rw-r--r--base/message_loop.cc40
-rw-r--r--base/message_loop.h35
-rw-r--r--base/message_pump_glib.cc17
-rw-r--r--base/message_pump_glib.h28
7 files changed, 294 insertions, 52 deletions
diff --git a/base/keyboard_codes.h b/base/keyboard_codes.h
index 2f66ac1..3d8700f 100644
--- a/base/keyboard_codes.h
+++ b/base/keyboard_codes.h
@@ -9,8 +9,10 @@
#if defined(OS_WIN)
#include "base/keyboard_codes_win.h"
-#elif defined(OS_POSIX)
-#include "base/keyboard_codes_posix.h"
+#elif defined(OS_LINUX)
+#include "base/keyboard_codes_linux.h"
+#elif defined(OS_MACOSX)
+#include "base/keyboard_codes_mac.h"
#endif
#endif // BASE_KEYBOARD_CODES_H_
diff --git a/base/keyboard_codes_linux.h b/base/keyboard_codes_linux.h
new file mode 100644
index 0000000..50b30b1
--- /dev/null
+++ b/base/keyboard_codes_linux.h
@@ -0,0 +1,214 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/*
+ * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com. All rights reserved.
+ * Copyright (C) 2008, 2009 Google Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR
+ * PROFITS, OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef BASE_KEYBOARD_CODES_LINUX_H_
+#define BASE_KEYBOARD_CODES_LINUX_H_
+
+#include <gdk/gdkkeysyms.h>
+#include <X11/XF86keysym.h>
+
+namespace base {
+
+enum {
+ VKEY_BACK = GDK_BackSpace,
+ VKEY_TAB = GDK_Tab,
+ VKEY_CLEAR = GDK_Clear,
+ VKEY_RETURN = GDK_Return,
+ VKEY_SHIFT = GDK_Shift_L, // TODO(jcampan): what about GDK_Shift_R?
+ VKEY_CONTROL = GDK_Control_L, // TODO(jcampan): what about GDK_Control_R?
+ VKEY_MENU = GDK_Menu,
+ VKEY_PAUSE = GDK_Pause,
+ VKEY_CAPITAL = GDK_Shift_Lock,
+ VKEY_KANA = GDK_Kana_Shift,
+ VKEY_HANGUL = GDK_Hangul,
+ // TODO(jcampan): not sure what the next 2 are.
+ VKEY_JUNJA = GDK_Hangul,
+ VKEY_FINAL = GDK_Hangul,
+ VKEY_HANJA = GDK_Hangul_Hanja,
+ VKEY_KANJI = GDK_Kanji,
+ VKEY_ESCAPE = GDK_Escape,
+ // TODO(jcampan): not sure what the next 4 are.
+ VKEY_CONVERT = 0x1C,
+ VKEY_NONCONVERT = 0x1D,
+ VKEY_ACCEPT = 0x1E,
+ VKEY_MODECHANGE = 0x1F,
+ VKEY_SPACE = GDK_space,
+ VKEY_PRIOR = GDK_Prior,
+ VKEY_NEXT = GDK_Next,
+ VKEY_END = GDK_End,
+ VKEY_HOME = GDK_Home,
+ VKEY_LEFT = GDK_Left,
+ VKEY_UP = GDK_Up,
+ VKEY_RIGHT = GDK_Right,
+ VKEY_DOWN = GDK_Down,
+ VKEY_SELECT = GDK_Select,
+ VKEY_PRINT = GDK_Print,
+ VKEY_EXECUTE = GDK_Execute,
+ VKEY_SNAPSHOT = 0x2C, // TODO(jcampan): not sure what this one is.
+ VKEY_INSERT = GDK_Insert,
+ VKEY_DELETE = GDK_Delete,
+ VKEY_HELP = GDK_Help,
+ VKEY_0 = GDK_0,
+ VKEY_1 = GDK_1,
+ VKEY_2 = GDK_2,
+ VKEY_3 = GDK_3,
+ VKEY_4 = GDK_4,
+ VKEY_5 = GDK_5,
+ VKEY_6 = GDK_6,
+ VKEY_7 = GDK_7,
+ VKEY_8 = GDK_8,
+ VKEY_9 = GDK_9,
+ VKEY_A = GDK_A,
+ VKEY_B = GDK_B,
+ VKEY_C = GDK_C,
+ VKEY_D = GDK_D,
+ VKEY_E = GDK_E,
+ VKEY_F = GDK_F,
+ VKEY_G = GDK_G,
+ VKEY_H = GDK_H,
+ VKEY_I = GDK_I,
+ VKEY_J = GDK_J,
+ VKEY_K = GDK_K,
+ VKEY_L = GDK_L,
+ VKEY_M = GDK_M,
+ VKEY_N = GDK_N,
+ VKEY_O = GDK_O,
+ VKEY_P = GDK_P,
+ VKEY_Q = GDK_Q,
+ VKEY_R = GDK_R,
+ VKEY_S = GDK_S,
+ VKEY_T = GDK_T,
+ VKEY_U = GDK_U,
+ VKEY_V = GDK_V,
+ VKEY_W = GDK_W,
+ VKEY_X = GDK_X,
+ VKEY_Y = GDK_Y,
+ VKEY_Z = GDK_Z,
+ VKEY_LWIN = GDK_Meta_L,
+ VKEY_RWIN = GDK_Meta_R,
+ VKEY_APPS = 0x5D, // TODO(jcampan): not sure what this one is.
+ VKEY_SLEEP = XF86XK_Sleep,
+ VKEY_NUMPAD0 = GDK_KP_0,
+ VKEY_NUMPAD1 = GDK_KP_1,
+ VKEY_NUMPAD2 = GDK_KP_2,
+ VKEY_NUMPAD3 = GDK_KP_3,
+ VKEY_NUMPAD4 = GDK_KP_4,
+ VKEY_NUMPAD5 = GDK_KP_5,
+ VKEY_NUMPAD6 = GDK_KP_6,
+ VKEY_NUMPAD7 = GDK_KP_7,
+ VKEY_NUMPAD8 = GDK_KP_8,
+ VKEY_NUMPAD9 = GDK_KP_9,
+ VKEY_MULTIPLY = GDK_KP_Multiply,
+ VKEY_ADD = GDK_KP_Add,
+ VKEY_SEPARATOR = GDK_KP_Separator,
+ VKEY_SUBTRACT = GDK_KP_Subtract,
+ VKEY_DECIMAL = GDK_KP_Decimal,
+ VKEY_DIVIDE = GDK_KP_Divide,
+ VKEY_F1 = GDK_F1,
+ VKEY_F2 = GDK_F2,
+ VKEY_F3 = GDK_F3,
+ VKEY_F4 = GDK_F4,
+ VKEY_F5 = GDK_F5,
+ VKEY_F6 = GDK_F6,
+ VKEY_F7 = GDK_F7,
+ VKEY_F8 = GDK_F8,
+ VKEY_F9 = GDK_F9,
+ VKEY_F10 = GDK_F10,
+ VKEY_F11 = GDK_F11,
+ VKEY_F12 = GDK_F12,
+ VKEY_F13 = GDK_F13,
+ VKEY_F14 = GDK_F14,
+ VKEY_F15 = GDK_F15,
+ VKEY_F16 = GDK_F16,
+ VKEY_F17 = GDK_F17,
+ VKEY_F18 = GDK_F18,
+ VKEY_F19 = GDK_F19,
+ VKEY_F20 = GDK_F20,
+ VKEY_F21 = GDK_F21,
+ VKEY_F22 = GDK_F22,
+ VKEY_F23 = GDK_F23,
+ VKEY_F24 = GDK_F24,
+ VKEY_NUMLOCK = GDK_Num_Lock,
+ VKEY_SCROLL = GDK_Scroll_Lock,
+ VKEY_LSHIFT = GDK_Shift_L,
+ VKEY_RSHIFT = GDK_Shift_R,
+ VKEY_LCONTROL = GDK_Control_L,
+ VKEY_RCONTROL = GDK_Control_R,
+ VKEY_LMENU = GDK_Alt_L,
+ VKEY_RMENU = GDK_Alt_R,
+ VKEY_BROWSER_BACK = XF86XK_Back,
+ VKEY_BROWSER_FORWARD = XF86XK_Forward,
+ VKEY_BROWSER_REFRESH = XF86XK_Refresh,
+ VKEY_BROWSER_STOP = XF86XK_Stop,
+ VKEY_BROWSER_SEARCH = XF86XK_Search,
+ VKEY_BROWSER_FAVORITES = XF86XK_Favorites,
+ VKEY_BROWSER_HOME = XF86XK_HomePage,
+ VKEY_VOLUME_MUTE = XF86XK_AudioMute,
+ VKEY_VOLUME_DOWN = XF86XK_AudioLowerVolume,
+ VKEY_VOLUME_UP = XF86XK_AudioRaiseVolume,
+ VKEY_MEDIA_NEXT_TRACK = XF86XK_AudioNext,
+ VKEY_MEDIA_PREV_TRACK = XF86XK_AudioPrev,
+ VKEY_MEDIA_STOP = XF86XK_AudioStop,
+ VKEY_MEDIA_PLAY_PAUSE = XF86XK_AudioPause,
+ VKEY_MEDIA_LAUNCH_MAIL = XF86XK_Mail,
+ VKEY_MEDIA_LAUNCH_MEDIA_SELECT = XF86XK_AudioMedia,
+ VKEY_MEDIA_LAUNCH_APP1 = XF86XK_Launch1,
+ VKEY_MEDIA_LAUNCH_APP2 = XF86XK_Launch2,
+ // TODO(jcampan): Figure-out values below.
+ VKEY_OEM_1 = 0xBA,
+ VKEY_OEM_PLUS = 0xBB,
+ VKEY_OEM_COMMA = 0xBC,
+ VKEY_OEM_MINUS = 0xBD,
+ VKEY_OEM_PERIOD = 0xBE,
+ VKEY_OEM_2 = 0xBF,
+ VKEY_OEM_3 = 0xC0,
+ VKEY_OEM_4 = 0xDB,
+ VKEY_OEM_5 = 0xDC,
+ VKEY_OEM_6 = 0xDD,
+ VKEY_OEM_7 = 0xDE,
+ VKEY_OEM_8 = 0xDF,
+ VKEY_OEM_102 = 0xE2,
+ VKEY_PROCESSKEY = 0xE5,
+ VKEY_PACKET = 0xE7,
+ VKEY_ATTN = 0xF6,
+ VKEY_CRSEL = 0xF7,
+ VKEY_EXSEL = 0xF8,
+ VKEY_EREOF = 0xF9,
+ VKEY_PLAY = 0xFA,
+ VKEY_ZOOM = XF86XK_ZoomIn,
+ VKEY_NONAME = 0xFC,
+ VKEY_PA1 = 0xFD,
+ VKEY_OEM_CLEAR = 0xFE,
+ VKEY_UNKNOWN = 0
+};
+
+} // namespace views
+
+#endif // BASE_KEYBOARD_CODES_LINUX_H_
diff --git a/base/keyboard_codes_posix.h b/base/keyboard_codes_mac.h
index 5100d96..9b414dc 100644
--- a/base/keyboard_codes_posix.h
+++ b/base/keyboard_codes_mac.h
@@ -28,8 +28,8 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef BASE_KEYBOARD_CODES_POSIX_H_
-#define BASE_KEYBOARD_CODES_POSIX_H_
+#ifndef BASE_KEYBOARD_CODES_MAC_H_
+#define BASE_KEYBOARD_CODES_MAC_H_
namespace base {
@@ -205,4 +205,4 @@ enum {
} // namespace views
-#endif // BASE_KEYBOARD_CODES_POSIX_H_
+#endif // BASE_KEYBOARD_CODES_MAC_H_
diff --git a/base/message_loop.cc b/base/message_loop.cc
index 4c91b1f..5464670 100644
--- a/base/message_loop.cc
+++ b/base/message_loop.cc
@@ -188,9 +188,10 @@ void MessageLoop::RunInternal() {
StartHistogrammer();
-#if defined(OS_WIN)
- if (state_->dispatcher) {
- pump_win()->RunWithDispatcher(this, state_->dispatcher);
+#if defined(OS_WIN) || defined(OS_LINUX)
+ if (state_->dispatcher && type() == TYPE_UI) {
+ static_cast<base::MessagePumpForUI*>(pump_.get())->
+ RunWithDispatcher(this, state_->dispatcher);
return;
}
#endif
@@ -480,7 +481,7 @@ MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) {
// Initialize the other fields:
quit_received = false;
-#if defined(OS_WIN)
+#if defined(OS_WIN) || defined(OS_LINUX)
dispatcher = NULL;
#endif
}
@@ -570,8 +571,20 @@ const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = {
//------------------------------------------------------------------------------
// MessageLoopForUI
-#if defined(OS_LINUX) || defined(OS_WIN)
+#if defined(OS_WIN)
+void MessageLoopForUI::WillProcessMessage(const MSG& message) {
+ pump_win()->WillProcessMessage(message);
+}
+void MessageLoopForUI::DidProcessMessage(const MSG& message) {
+ pump_win()->DidProcessMessage(message);
+}
+void MessageLoopForUI::PumpOutPendingPaintMessages() {
+ pump_ui()->PumpOutPendingPaintMessages();
+}
+
+#endif // defined(OS_WIN)
+#if defined(OS_LINUX) || defined(OS_WIN)
void MessageLoopForUI::AddObserver(Observer* observer) {
pump_ui()->AddObserver(observer);
}
@@ -580,27 +593,12 @@ void MessageLoopForUI::RemoveObserver(Observer* observer) {
pump_ui()->RemoveObserver(observer);
}
-#endif
-
-#if defined(OS_WIN)
-
void MessageLoopForUI::Run(Dispatcher* dispatcher) {
AutoRunState save_state(this);
state_->dispatcher = dispatcher;
RunHandler();
}
-
-void MessageLoopForUI::WillProcessMessage(const MSG& message) {
- pump_win()->WillProcessMessage(message);
-}
-void MessageLoopForUI::DidProcessMessage(const MSG& message) {
- pump_win()->DidProcessMessage(message);
-}
-void MessageLoopForUI::PumpOutPendingPaintMessages() {
- pump_ui()->PumpOutPendingPaintMessages();
-}
-
-#endif // defined(OS_WIN)
+#endif // defined(OS_LINUX) || defined(OS_WIN)
//------------------------------------------------------------------------------
// MessageLoopForIO
diff --git a/base/message_loop.h b/base/message_loop.h
index debbade..395ce6e 100644
--- a/base/message_loop.h
+++ b/base/message_loop.h
@@ -234,6 +234,14 @@ class MessageLoop : public base::MessagePump::Delegate {
// Returns true if we are currently running a nested message loop.
bool IsNested();
+#if defined(OS_WIN)
+ typedef base::MessagePumpWin::Dispatcher Dispatcher;
+ typedef base::MessagePumpWin::Observer Observer;
+#elif defined(OS_LINUX)
+ typedef base::MessagePumpForUI::Dispatcher Dispatcher;
+ typedef base::MessagePumpForUI::Observer Observer;
+#endif
+
//----------------------------------------------------------------------------
protected:
struct RunState {
@@ -244,8 +252,8 @@ class MessageLoop : public base::MessagePump::Delegate {
// once it becomes idle.
bool quit_received;
-#if defined(OS_WIN)
- base::MessagePumpWin::Dispatcher* dispatcher;
+#if defined(OS_WIN) || defined(OS_LINUX)
+ Dispatcher* dispatcher;
#endif
};
@@ -416,34 +424,25 @@ class MessageLoopForUI : public MessageLoop {
return static_cast<MessageLoopForUI*>(loop);
}
-#if defined(OS_LINUX)
- typedef base::MessagePumpForUI::Observer Observer;
-
- // See message_pump_glib for definitions of these methods.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
-#endif
-
#if defined(OS_WIN)
- typedef base::MessagePumpWin::Dispatcher Dispatcher;
- typedef base::MessagePumpWin::Observer Observer;
-
- // Please see MessagePumpWin for definitions of these methods.
- void AddObserver(Observer* observer);
- void RemoveObserver(Observer* observer);
- void Run(Dispatcher* dispatcher);
void WillProcessMessage(const MSG& message);
void DidProcessMessage(const MSG& message);
void PumpOutPendingPaintMessages();
#endif
#if defined(OS_WIN) || defined(OS_LINUX)
+ // Please see message_pump_win/message_pump_glib for definitions of these
+ // methods.
+ void AddObserver(Observer* observer);
+ void RemoveObserver(Observer* observer);
+ void Run(Dispatcher* dispatcher);
+
protected:
// TODO(rvargas): Make this platform independent.
base::MessagePumpForUI* pump_ui() {
return static_cast<base::MessagePumpForUI*>(pump_.get());
}
-#endif // defined(OS_WIN)
+#endif // defined(OS_WIN) || defined(OS_LINUX)
};
// Do not add any member variables to MessageLoopForUI! This is important b/c
diff --git a/base/message_pump_glib.cc b/base/message_pump_glib.cc
index ac0a080..6f050ba 100644
--- a/base/message_pump_glib.cc
+++ b/base/message_pump_glib.cc
@@ -153,7 +153,8 @@ MessagePumpForUI::~MessagePumpForUI() {
close(wakeup_pipe_write_);
}
-void MessagePumpForUI::Run(Delegate* delegate) {
+void MessagePumpForUI::RunWithDispatcher(Delegate* delegate,
+ Dispatcher* dispatcher) {
#ifndef NDEBUG
// Make sure we only run this on one thread. GTK only has one message pump
// so we can only have one UI loop per process.
@@ -165,6 +166,7 @@ void MessagePumpForUI::Run(Delegate* delegate) {
RunState state;
state.delegate = delegate;
+ state.dispatcher = dispatcher;
state.should_quit = false;
state.run_depth = state_ ? state_->run_depth + 1 : 1;
state.has_work = false;
@@ -309,9 +311,16 @@ void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) {
// static
void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) {
- reinterpret_cast<MessagePumpForUI*>(data)->WillProcessEvent(event);
- gtk_main_do_event(event);
- reinterpret_cast<MessagePumpForUI*>(data)->DidProcessEvent(event);
+ MessagePumpForUI* message_pump = reinterpret_cast<MessagePumpForUI*>(data);
+
+ message_pump->WillProcessEvent(event);
+ if (message_pump->state_->dispatcher) {
+ if (!message_pump->state_->dispatcher->Dispatch(event))
+ message_pump->state_->should_quit = true;
+ } else {
+ gtk_main_do_event(event);
+ }
+ message_pump->DidProcessEvent(event);
}
} // namespace base
diff --git a/base/message_pump_glib.h b/base/message_pump_glib.h
index e8288a8..08e1964 100644
--- a/base/message_pump_glib.h
+++ b/base/message_pump_glib.h
@@ -31,10 +31,29 @@ class MessagePumpForUI : public MessagePump {
virtual void DidProcessEvent(GdkEvent* event) = 0;
};
+ // Dispatcher is used during a nested invocation of Run to dispatch events.
+ // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not
+ // dispatch events (or invoke gtk_main_do_event), rather every event is
+ // passed to Dispatcher's Dispatch method for dispatch. It is up to the
+ // Dispatcher to dispatch, or not, the event.
+ //
+ // The nested loop is exited by either posting a quit, or returning false
+ // from Dispatch.
+ class Dispatcher {
+ public:
+ virtual ~Dispatcher() {}
+ // Dispatches the event. If true is returned processing continues as
+ // normal. If false is returned, the nested loop exits immediately.
+ virtual bool Dispatch(GdkEvent* event) = 0;
+ };
+
MessagePumpForUI();
- ~MessagePumpForUI();
+ virtual ~MessagePumpForUI();
+
+ // Like MessagePump::Run, but GdkEvent objects are routed through dispatcher.
+ virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher);
- virtual void Run(Delegate* delegate);
+ virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); }
virtual void Quit();
virtual void ScheduleWork();
virtual void ScheduleDelayedWork(const Time& delayed_work_time);
@@ -49,10 +68,10 @@ class MessagePumpForUI : public MessagePump {
bool HandleCheck();
void HandleDispatch();
- // Add an Observer, which will start receiving notifications immediately.
+ // Adds an Observer, which will start receiving notifications immediately.
void AddObserver(Observer* observer);
- // Remove an Observer. It is safe to call this method while an Observer is
+ // Removes an Observer. It is safe to call this method while an Observer is
// receiving a notification callback.
void RemoveObserver(Observer* observer);
@@ -61,6 +80,7 @@ class MessagePumpForUI : public MessagePump {
// separate between them in this structure type.
struct RunState {
Delegate* delegate;
+ Dispatcher* dispatcher;
// Used to flag that the current Run() invocation should return ASAP.
bool should_quit;