mirror of
https://github.com/genodelabs/genode.git
synced 2025-06-18 07:08:18 +00:00
nitpicker: Support for global keys
This commit is contained in:
@ -102,6 +102,15 @@ append config {
|
|||||||
<start name="nitpicker">
|
<start name="nitpicker">
|
||||||
<resource name="RAM" quantum="1M"/>
|
<resource name="RAM" quantum="1M"/>
|
||||||
<provides><service name="Nitpicker"/></provides>
|
<provides><service name="Nitpicker"/></provides>
|
||||||
|
<config>
|
||||||
|
<global-keys>
|
||||||
|
<key name="KEY_SCROLLLOCK" operation="xray" />
|
||||||
|
<key name="KEY_SYSRQ" operation="kill" />
|
||||||
|
<key name="KEY_PRINT" operation="kill" />
|
||||||
|
<key name="KEY_F11" operation="kill" />
|
||||||
|
<key name="KEY_F12" operation="xray" />
|
||||||
|
</global-keys>
|
||||||
|
</config>
|
||||||
</start>
|
</start>
|
||||||
<start name="launchpad">
|
<start name="launchpad">
|
||||||
<resource name="RAM" quantum="32M"/>
|
<resource name="RAM" quantum="32M"/>
|
||||||
|
@ -12,15 +12,56 @@ Configuration
|
|||||||
Nitpicker supports the following configuration options, supplied via
|
Nitpicker supports the following configuration options, supplied via
|
||||||
Genode's config mechanism.
|
Genode's config mechanism.
|
||||||
|
|
||||||
:Tinting of clients in X-Ray mode:
|
Tinting of clients in X-Ray mode
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
Nitpicker allows for assigning a color to single clients or a groups
|
Nitpicker allows for assigning a color to single clients or a groups
|
||||||
of clients based on the client's label. The following configuration
|
of clients based on the client's label. The following configuration
|
||||||
tints all views of the launchpad subsystem in blue except for those
|
tints all views of the launchpad subsystem in blue except for those
|
||||||
views that belong to the testnit child of launchpad.
|
views that belong to the testnit child of launchpad.
|
||||||
! <config>
|
|
||||||
! <policy label="launchpad" color="#0000ff"/>
|
|
||||||
! <policy label="launchpad -> testnit" color="#ff0000"/>
|
|
||||||
! </config>
|
|
||||||
|
|
||||||
|
! <config>
|
||||||
|
! <policy label="launchpad" color="#0000ff"/>
|
||||||
|
! <policy label="launchpad -> testnit" color="#ff0000"/>
|
||||||
|
! </config>
|
||||||
|
|
||||||
|
Global key definitions
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Nitpicker has a few built-in function that can be activated via global
|
||||||
|
keyboard shortcuts, namely the X-ray mode and the kill mode. The keys
|
||||||
|
for toggling those functions can be defined as follows:
|
||||||
|
|
||||||
|
! <config>
|
||||||
|
! <global-keys>
|
||||||
|
! <key name="KEY_SCROLLLOCK" operation="xray" />
|
||||||
|
! <key name="KEY_PRINT" operation="kill" />
|
||||||
|
! </global-keys>
|
||||||
|
! </config>
|
||||||
|
|
||||||
|
The '<global-keys>' node contains the policy for handling global keys. Each
|
||||||
|
'<key>' subnode expresses a rule for named key. The 'operation' attribute
|
||||||
|
refers nitpicker's built-in operations. In the example above, the X-ray
|
||||||
|
mode can be activated via the scroll-lock key and the kill mode can be
|
||||||
|
activated via the print key.
|
||||||
|
|
||||||
|
Alternatively to specifying an 'operation' attribute, a key node can contain
|
||||||
|
a 'label' attribute. If specified, all events regarding the key will be
|
||||||
|
reported to the client with the specified label. This enables clients to
|
||||||
|
handle global shortcuts. The client with the matching label will receive
|
||||||
|
all events until the number of concurrently pressed keys reaches zero.
|
||||||
|
This way, it is possible to handle chords of multiple keys starting with
|
||||||
|
the key specified in the '<key>' node. For the routing of global keys to
|
||||||
|
clients, the order of '<key>' nodes is important. If multiple nodes exists for
|
||||||
|
different labels, the first match will take effect. For example:
|
||||||
|
|
||||||
|
! <global-keys>
|
||||||
|
! <key name="KEY_F11" label="launchpad -> testnit" />
|
||||||
|
! <key name="KEY_F11" label="launchpad" />
|
||||||
|
! </global-keys>
|
||||||
|
|
||||||
|
The "launchpad" client will receive all key sequences starting with F11 unless
|
||||||
|
the "launchpad -> testnit" program is running. As soon as testnit gets started
|
||||||
|
by launchpad, testnit will receive the events. If the order was reversed,
|
||||||
|
launchpad would always receive the events.
|
||||||
|
|
||||||
|
@ -13,26 +13,21 @@
|
|||||||
|
|
||||||
#include <input/event.h>
|
#include <input/event.h>
|
||||||
#include <input/keycodes.h>
|
#include <input/keycodes.h>
|
||||||
|
|
||||||
#include "user_state.h"
|
#include "user_state.h"
|
||||||
|
|
||||||
using namespace Input;
|
using namespace Input;
|
||||||
|
|
||||||
/*
|
|
||||||
* Definition of magic keys
|
|
||||||
*/
|
|
||||||
enum { KILL_KEY = KEY_PRINT };
|
|
||||||
enum { XRAY_KEY = KEY_SCROLLLOCK };
|
|
||||||
|
|
||||||
|
|
||||||
/***************
|
/***************
|
||||||
** Utilities **
|
** Utilities **
|
||||||
***************/
|
***************/
|
||||||
|
|
||||||
static inline bool _masked_key(int keycode) {
|
static inline bool _masked_key(Global_keys &global_keys, Keycode keycode) {
|
||||||
return keycode == KILL_KEY || keycode == XRAY_KEY; }
|
return global_keys.is_kill_key(keycode) || global_keys.is_xray_key(keycode); }
|
||||||
|
|
||||||
|
|
||||||
static inline bool _mouse_button(int keycode) {
|
static inline bool _mouse_button(Keycode keycode) {
|
||||||
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
|
return keycode >= BTN_LEFT && keycode <= BTN_MIDDLE; }
|
||||||
|
|
||||||
|
|
||||||
@ -40,27 +35,27 @@ static inline bool _mouse_button(int keycode) {
|
|||||||
** User state interface **
|
** User state interface **
|
||||||
**************************/
|
**************************/
|
||||||
|
|
||||||
User_state::User_state(Canvas *canvas, Menubar *menubar):
|
User_state::User_state(Global_keys &global_keys, Canvas *canvas, Menubar *menubar)
|
||||||
View_stack(canvas, this), _key_cnt(0), _menubar(menubar),
|
:
|
||||||
_pointed_view(0) { }
|
View_stack(canvas, this), _global_keys(global_keys), _key_cnt(0),
|
||||||
|
_menubar(menubar), _pointed_view(0), _input_receiver(0),
|
||||||
|
_global_key_sequence(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
|
||||||
void User_state::handle_event(Input::Event ev)
|
void User_state::handle_event(Input::Event ev)
|
||||||
{
|
{
|
||||||
|
Input::Keycode const keycode = ev.keycode();
|
||||||
|
Input::Event::Type const type = ev.type();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mangle incoming events
|
* Mangle incoming events
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int keycode = ev.code();
|
|
||||||
int ax = _mouse_pos.x(), ay = _mouse_pos.y();
|
int ax = _mouse_pos.x(), ay = _mouse_pos.y();
|
||||||
int rx = 0, ry = 0; /* skip info about relative motion per default */
|
int rx = 0, ry = 0; /* skip info about relative motion per default */
|
||||||
|
|
||||||
/* KEY_PRINT and KEY_SYSRQ both enter kill mode */
|
|
||||||
if ((ev.type() == Event::PRESS) && (ev.code() == KEY_SYSRQ))
|
|
||||||
keycode = KEY_PRINT;
|
|
||||||
|
|
||||||
/* transparently handle absolute and relative motion events */
|
/* transparently handle absolute and relative motion events */
|
||||||
if (ev.type() == Event::MOTION) {
|
if (type == Event::MOTION) {
|
||||||
if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) {
|
if ((ev.rx() || ev.ry()) && ev.ax() == 0 && ev.ay() == 0) {
|
||||||
ax = max(0, min(size().w(), ax + ev.rx()));
|
ax = max(0, min(size().w(), ax + ev.rx()));
|
||||||
ay = max(0, min(size().h(), ay + ev.ry()));
|
ay = max(0, min(size().h(), ay + ev.ry()));
|
||||||
@ -71,17 +66,17 @@ void User_state::handle_event(Input::Event ev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* propagate relative motion for wheel events */
|
/* propagate relative motion for wheel events */
|
||||||
if (ev.type() == Event::WHEEL) {
|
if (type == Event::WHEEL) {
|
||||||
rx = ev.rx();
|
rx = ev.rx();
|
||||||
ry = ev.ry();
|
ry = ev.ry();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create the mangled event */
|
/* create the mangled event */
|
||||||
ev = Input::Event(ev.type(), keycode, ax, ay, rx, ry);
|
ev = Input::Event(type, keycode, ax, ay, rx, ry);
|
||||||
|
|
||||||
_mouse_pos = Point(ax, ay);
|
_mouse_pos = Point(ax, ay);
|
||||||
|
|
||||||
View *pointed_view = find_view(_mouse_pos);
|
View * pointed_view = find_view(_mouse_pos);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deliver a leave event if pointed-to session changed
|
* Deliver a leave event if pointed-to session changed
|
||||||
@ -95,32 +90,54 @@ void User_state::handle_event(Input::Event ev)
|
|||||||
/* remember currently pointed-at view */
|
/* remember currently pointed-at view */
|
||||||
_pointed_view = pointed_view;
|
_pointed_view = pointed_view;
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* We expect pointed view to be always defined. In the worst case (with no
|
* Guard that performs a whole-screen update when leaving the scope
|
||||||
* view at all), the pointed view is the background.
|
|
||||||
*/
|
*/
|
||||||
|
struct Update_all_guard
|
||||||
|
{
|
||||||
|
User_state &user_state;
|
||||||
|
bool enabled;
|
||||||
|
char const *menu_title;
|
||||||
|
|
||||||
bool update_all = false;
|
Update_all_guard(User_state &user_state)
|
||||||
|
: user_state(user_state), enabled(false), menu_title("") { }
|
||||||
|
|
||||||
|
~Update_all_guard()
|
||||||
|
{
|
||||||
|
if (!enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (user_state._input_receiver)
|
||||||
|
user_state._menubar->state(user_state, user_state._input_receiver->label(),
|
||||||
|
menu_title, user_state._input_receiver->color());
|
||||||
|
else
|
||||||
|
user_state._menubar->state(user_state, "", "", BLACK);
|
||||||
|
|
||||||
|
user_state.update_all_views();
|
||||||
|
}
|
||||||
|
} update_all_guard(*this);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Detect mouse press event in kill mode, used to select the session
|
* Detect mouse press event in kill mode, used to select the session
|
||||||
* to lock out.
|
* to lock out.
|
||||||
*/
|
*/
|
||||||
if (kill() && ev.type() == Event::PRESS && ev.code() == Input::BTN_LEFT) {
|
if (kill() && type == Event::PRESS && keycode == Input::BTN_LEFT) {
|
||||||
if (pointed_view && pointed_view->session())
|
if (pointed_view && pointed_view->session())
|
||||||
lock_out_session(pointed_view->session());
|
lock_out_session(pointed_view->session());
|
||||||
|
|
||||||
/* leave kill mode */
|
/* leave kill mode */
|
||||||
pointed_view = 0;
|
pointed_view = 0;
|
||||||
Mode::_mode &= ~KILL;
|
Mode::_mode &= ~KILL;
|
||||||
update_all = true;
|
update_all_guard.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ev.type() == Event::PRESS && _key_cnt == 0) {
|
/*
|
||||||
|
* Handle start of a key sequence
|
||||||
|
*/
|
||||||
|
if (type == Event::PRESS && _key_cnt == 0) {
|
||||||
|
|
||||||
/* update focused view */
|
/* update focused view */
|
||||||
if (pointed_view != focused_view()
|
if (pointed_view != focused_view() && _mouse_button(keycode)) {
|
||||||
&& _mouse_button(ev.code())) {
|
|
||||||
|
|
||||||
bool const focus_stays_in_session =
|
bool const focus_stays_in_session =
|
||||||
(_focused_view && pointed_view &&
|
(_focused_view && pointed_view &&
|
||||||
@ -131,7 +148,7 @@ void User_state::handle_event(Input::Event ev)
|
|||||||
* changing the focus to another session.
|
* changing the focus to another session.
|
||||||
*/
|
*/
|
||||||
if (flat() && !focus_stays_in_session)
|
if (flat() && !focus_stays_in_session)
|
||||||
update_all = true;
|
update_all_guard.enabled = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Notify both the old focussed session and the new one.
|
* Notify both the old focussed session and the new one.
|
||||||
@ -150,43 +167,63 @@ void User_state::handle_event(Input::Event ev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!flat() || !_focused_view || !pointed_view)
|
if (!flat() || !_focused_view || !pointed_view)
|
||||||
update_all = true;
|
update_all_guard.enabled = true;
|
||||||
|
|
||||||
_focused_view = pointed_view;
|
_focused_view = pointed_view;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* toggle kill and xray modes */
|
/*
|
||||||
if (ev.code() == KILL_KEY || ev.code() == XRAY_KEY) {
|
* If there exists a global rule for the pressed key, set the
|
||||||
|
* corresponding session as receiver of the input stream until the key
|
||||||
Mode::_mode ^= ev.code() == KILL_KEY ? KILL : XRAY;
|
* count reaches zero. Otherwise, the input stream is directed to the
|
||||||
update_all = true;
|
* pointed-at view.
|
||||||
}
|
*
|
||||||
|
* If we deliver a global key sequence, we temporarily change the focus
|
||||||
|
* to the global receiver. To reflect that change, we need to update
|
||||||
|
* the whole screen.
|
||||||
|
*/
|
||||||
|
Session * const global_receiver = _global_keys.global_receiver(keycode);
|
||||||
|
if (global_receiver) {
|
||||||
|
_global_key_sequence = true;
|
||||||
|
_input_receiver = global_receiver;
|
||||||
|
update_all_guard.menu_title = "";
|
||||||
|
update_all_guard.enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (update_all) {
|
/*
|
||||||
|
* No global rule matched, so the input stream gets directed to the
|
||||||
|
* focused view or refers to a built-in operation.
|
||||||
|
*/
|
||||||
|
if (!global_receiver && _focused_view) {
|
||||||
|
_input_receiver = _focused_view->session();
|
||||||
|
update_all_guard.menu_title = _focused_view->title();
|
||||||
|
}
|
||||||
|
|
||||||
if (focused_view() && focused_view()->session())
|
/*
|
||||||
_menubar->state(*this, focused_view()->session()->label(),
|
* Toggle kill and xray modes. If one of those keys is pressed,
|
||||||
focused_view()->title(),
|
* suppress the delivery to clients.
|
||||||
focused_view()->session()->color());
|
*/
|
||||||
else
|
if (_global_keys.is_operation_key(keycode)) {
|
||||||
_menubar->state(*this, "", "", BLACK);
|
|
||||||
|
|
||||||
update_all_views();
|
Mode::_mode ^= _global_keys.is_kill_key(keycode) ? KILL : 0
|
||||||
|
| _global_keys.is_xray_key(keycode) ? XRAY : 0;
|
||||||
|
|
||||||
|
update_all_guard.enabled = true;
|
||||||
|
_input_receiver = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* count keys */
|
/* count keys */
|
||||||
if (ev.type() == Event::PRESS) _key_cnt++;
|
if (type == Event::PRESS) _key_cnt++;
|
||||||
if (ev.type() == Event::RELEASE && _key_cnt > 0) _key_cnt--;
|
if (type == Event::RELEASE && _key_cnt > 0) _key_cnt--;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Deliver event to Nitpicker session.
|
* Deliver event to Nitpicker session except when kill mode is activated
|
||||||
* (except when kill mode is activated)
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (kill()) return;
|
if (kill()) return;
|
||||||
|
|
||||||
if (ev.type() == Event::MOTION || ev.type() == Event::WHEEL) {
|
if (type == Event::MOTION || type == Event::WHEEL) {
|
||||||
|
|
||||||
if (_key_cnt == 0) {
|
if (_key_cnt == 0) {
|
||||||
|
|
||||||
@ -199,14 +236,24 @@ void User_state::handle_event(Input::Event ev)
|
|||||||
if (pointed_view)
|
if (pointed_view)
|
||||||
pointed_view->session()->submit_input_event(&ev);
|
pointed_view->session()->submit_input_event(&ev);
|
||||||
|
|
||||||
} else if (focused_view())
|
} else if (_input_receiver)
|
||||||
focused_view()->session()->submit_input_event(&ev);
|
_input_receiver->submit_input_event(&ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* deliver press/release event to session with focused view */
|
/* deliver press/release event to session with focused view */
|
||||||
if (ev.type() == Event::PRESS || ev.type() == Event::RELEASE)
|
if (type == Event::PRESS || type == Event::RELEASE)
|
||||||
if (!_masked_key(ev.code()) && focused_view())
|
if (_input_receiver)
|
||||||
focused_view()->session()->submit_input_event(&ev);
|
_input_receiver->submit_input_event(&ev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Detect end of global key sequence
|
||||||
|
*/
|
||||||
|
if (ev.type() == Event::RELEASE && _key_cnt == 0 && _global_key_sequence) {
|
||||||
|
_input_receiver = _focused_view ? _focused_view->session() : 0;
|
||||||
|
update_all_guard.menu_title = _focused_view ? _focused_view->title() : "";
|
||||||
|
update_all_guard.enabled = true;
|
||||||
|
_global_key_sequence = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -221,6 +268,8 @@ void User_state::forget(View *v)
|
|||||||
_menubar->state(*this, "", "", BLACK);
|
_menubar->state(*this, "", "", BLACK);
|
||||||
update_all_views();
|
update_all_views();
|
||||||
}
|
}
|
||||||
|
if (_input_receiver == v->session())
|
||||||
|
_input_receiver = 0;
|
||||||
if (_pointed_view == v)
|
if (_pointed_view == v)
|
||||||
_pointed_view = find_view(_mouse_pos);
|
_pointed_view = find_view(_mouse_pos);
|
||||||
}
|
}
|
||||||
|
@ -553,6 +553,8 @@ namespace Nitpicker {
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
Session_list &_session_list;
|
||||||
|
Global_keys &_global_keys;
|
||||||
Area _scr_size;
|
Area _scr_size;
|
||||||
View_stack *_view_stack;
|
View_stack *_view_stack;
|
||||||
Flush_merger *_flush_merger;
|
Flush_merger *_flush_merger;
|
||||||
@ -595,12 +597,17 @@ namespace Nitpicker {
|
|||||||
|
|
||||||
bool provides_default_bg = (Genode::strcmp(label_buf, "backdrop") == 0);
|
bool provides_default_bg = (Genode::strcmp(label_buf, "backdrop") == 0);
|
||||||
|
|
||||||
return new (md_alloc())
|
Session_component *session = new (md_alloc())
|
||||||
Session_component(label_buf, cdt, cdt, _view_stack, ep(),
|
Session_component(label_buf, cdt, cdt, _view_stack, ep(),
|
||||||
_flush_merger, _framebuffer, v_offset,
|
_flush_merger, _framebuffer, v_offset,
|
||||||
cdt->input_mask_buffer(),
|
cdt->input_mask_buffer(),
|
||||||
provides_default_bg, session_color(args),
|
provides_default_bg, session_color(args),
|
||||||
stay_top);
|
stay_top);
|
||||||
|
|
||||||
|
_session_list.insert(session);
|
||||||
|
_global_keys.apply_config(_session_list);
|
||||||
|
|
||||||
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _destroy_session(Session_component *session)
|
void _destroy_session(Session_component *session)
|
||||||
@ -608,6 +615,9 @@ namespace Nitpicker {
|
|||||||
Chunky_dataspace_texture<PT> *cdt;
|
Chunky_dataspace_texture<PT> *cdt;
|
||||||
cdt = static_cast<Chunky_dataspace_texture<PT> *>(session->texture());
|
cdt = static_cast<Chunky_dataspace_texture<PT> *>(session->texture());
|
||||||
|
|
||||||
|
_session_list.remove(session);
|
||||||
|
_global_keys.apply_config(_session_list);
|
||||||
|
|
||||||
destroy(md_alloc(), session);
|
destroy(md_alloc(), session);
|
||||||
destroy(md_alloc(), cdt);
|
destroy(md_alloc(), cdt);
|
||||||
}
|
}
|
||||||
@ -617,12 +627,14 @@ namespace Nitpicker {
|
|||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
Root(Genode::Rpc_entrypoint *session_ep, Area scr_size,
|
Root(Session_list &session_list, Global_keys &global_keys,
|
||||||
|
Genode::Rpc_entrypoint *session_ep, Area scr_size,
|
||||||
View_stack *view_stack, Genode::Allocator *md_alloc,
|
View_stack *view_stack, Genode::Allocator *md_alloc,
|
||||||
Flush_merger *flush_merger,
|
Flush_merger *flush_merger,
|
||||||
Framebuffer::Session *framebuffer, int default_v_offset)
|
Framebuffer::Session *framebuffer, int default_v_offset)
|
||||||
:
|
:
|
||||||
Genode::Root_component<Session_component>(session_ep, md_alloc),
|
Genode::Root_component<Session_component>(session_ep, md_alloc),
|
||||||
|
_session_list(session_list), _global_keys(global_keys),
|
||||||
_scr_size(scr_size), _view_stack(view_stack), _flush_merger(flush_merger),
|
_scr_size(scr_size), _view_stack(view_stack), _flush_merger(flush_merger),
|
||||||
_framebuffer(framebuffer), _default_v_offset(default_v_offset) { }
|
_framebuffer(framebuffer), _default_v_offset(default_v_offset) { }
|
||||||
};
|
};
|
||||||
@ -789,6 +801,85 @@ class Input_handler_component : public Genode::Rpc_object<Input_handler,
|
|||||||
typedef Pixel_rgb565 PT; /* physical pixel type */
|
typedef Pixel_rgb565 PT; /* physical pixel type */
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
** Handling of global keys **
|
||||||
|
*****************************/
|
||||||
|
|
||||||
|
Global_keys::Policy *Global_keys::_lookup_policy(char const *key_name)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < NUM_POLICIES; i++)
|
||||||
|
if (Genode::strcmp(key_name, Input::key_name((Input::Keycode)i)) == 0)
|
||||||
|
return &_policies[i];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Global_keys::apply_config(Session_list &session_list)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < NUM_POLICIES; i++)
|
||||||
|
_policies[i].undefine();
|
||||||
|
|
||||||
|
using Genode::Xml_node;
|
||||||
|
try {
|
||||||
|
Xml_node node = Genode::config()->xml_node().sub_node("global-keys").sub_node("key");
|
||||||
|
|
||||||
|
for (; ; node = node.next("key")) {
|
||||||
|
|
||||||
|
if (!node.has_attribute("name")) {
|
||||||
|
PWRN("attribute 'name' missing in <key> config node");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
char name[32]; name[0] = 0;
|
||||||
|
node.attribute("name").value(name, sizeof(name));
|
||||||
|
|
||||||
|
Policy * policy = _lookup_policy(name);
|
||||||
|
if (!policy) {
|
||||||
|
PWRN("invalid key name \"%s\"", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if two policies match, give precedence to policy defined first */
|
||||||
|
if (policy->defined())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (node.has_attribute("operation")) {
|
||||||
|
Xml_node::Attribute operation = node.attribute("operation");
|
||||||
|
|
||||||
|
if (operation.has_value("kill")) {
|
||||||
|
policy->operation_kill();
|
||||||
|
continue;
|
||||||
|
} else if (operation.has_value("xray")) {
|
||||||
|
policy->operation_xray();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
char buf[32]; buf[0] = 0;
|
||||||
|
operation.value(buf, sizeof(buf));
|
||||||
|
PWRN("unknown operation \"%s\" for key %s", buf, name);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!node.has_attribute("label")) {
|
||||||
|
PWRN("missing 'label' attribute for key %s", name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* assign policy to matching client session */
|
||||||
|
for (Session *s = session_list.first(); s; s = s->next())
|
||||||
|
if (node.attribute("label").has_value(s->label()))
|
||||||
|
policy->client(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Xml_node::Nonexistent_sub_node) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/******************
|
||||||
|
** Main program **
|
||||||
|
******************/
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
using namespace Genode;
|
using namespace Genode;
|
||||||
@ -828,7 +919,18 @@ int main(int argc, char **argv)
|
|||||||
PT *menubar_pixels = (PT *)env()->heap()->alloc(sizeof(PT)*mode.width()*16);
|
PT *menubar_pixels = (PT *)env()->heap()->alloc(sizeof(PT)*mode.width()*16);
|
||||||
Chunky_menubar<PT> menubar(menubar_pixels, Area(mode.width(), MENUBAR_HEIGHT));
|
Chunky_menubar<PT> menubar(menubar_pixels, Area(mode.width(), MENUBAR_HEIGHT));
|
||||||
|
|
||||||
User_state user_state(&screen, &menubar);
|
static Global_keys global_keys;
|
||||||
|
|
||||||
|
static Session_list session_list;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Apply initial global-key policy to turn X-ray and kill keys into
|
||||||
|
* effect. The policy will be updated each time the config changes,
|
||||||
|
* or when a session appears or disappears.
|
||||||
|
*/
|
||||||
|
global_keys.apply_config(session_list);
|
||||||
|
|
||||||
|
static User_state user_state(global_keys, &screen, &menubar);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create view stack with default elements
|
* Create view stack with default elements
|
||||||
@ -852,7 +954,8 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
|
Sliced_heap sliced_heap(env()->ram_session(), env()->rm_session());
|
||||||
|
|
||||||
static Nitpicker::Root<PT> np_root(&ep, Area(mode.width(), mode.height()),
|
static Nitpicker::Root<PT> np_root(session_list, global_keys,
|
||||||
|
&ep, Area(mode.width(), mode.height()),
|
||||||
&user_state, &sliced_heap,
|
&user_state, &sliced_heap,
|
||||||
&screen, &framebuffer,
|
&screen, &framebuffer,
|
||||||
MENUBAR_HEIGHT);
|
MENUBAR_HEIGHT);
|
||||||
|
96
os/src/server/nitpicker/include/global_keys.h
Normal file
96
os/src/server/nitpicker/include/global_keys.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* \brief Global keys policy
|
||||||
|
* \author Norman Feske
|
||||||
|
* \date 2013-09-06
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Genode Labs GmbH
|
||||||
|
*
|
||||||
|
* This file is part of the Genode OS framework, which is distributed
|
||||||
|
* under the terms of the GNU General Public License version 2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GLOBAL_KEYS_H_
|
||||||
|
#define _GLOBAL_KEYS_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <input/keycodes.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
|
#include "session.h"
|
||||||
|
|
||||||
|
class Global_keys
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct Policy
|
||||||
|
{
|
||||||
|
enum Type {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key is not global but should be propagated to focused client
|
||||||
|
*/
|
||||||
|
UNDEFINED,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key activates nitpicker's built-in kill mode
|
||||||
|
*/
|
||||||
|
KILL,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key activates nitpicker's built-in X-ray mode
|
||||||
|
*/
|
||||||
|
XRAY,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key should be propagated to client session
|
||||||
|
*/
|
||||||
|
CLIENT
|
||||||
|
};
|
||||||
|
|
||||||
|
Type _type;
|
||||||
|
Session *_session;
|
||||||
|
|
||||||
|
Policy() : _type(UNDEFINED), _session(0) { }
|
||||||
|
|
||||||
|
void undefine() { _type = UNDEFINED; _session = 0; }
|
||||||
|
void operation_kill() { _type = KILL; _session = 0; }
|
||||||
|
void operation_xray() { _type = XRAY; _session = 0; }
|
||||||
|
void client(Session *s) { _type = CLIENT; _session = s; }
|
||||||
|
|
||||||
|
bool defined() const { return _type != UNDEFINED; }
|
||||||
|
bool xray() const { return _type == XRAY; }
|
||||||
|
bool kill() const { return _type == KILL; }
|
||||||
|
};
|
||||||
|
|
||||||
|
enum { NUM_POLICIES = Input::KEY_MAX + 1 };
|
||||||
|
|
||||||
|
Policy _policies[NUM_POLICIES];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup policy that matches the specified key name
|
||||||
|
*/
|
||||||
|
Policy *_lookup_policy(char const *key_name);
|
||||||
|
|
||||||
|
bool _valid(Input::Keycode key) const {
|
||||||
|
return key >= 0 && key <= Input::KEY_MAX; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Session *global_receiver(Input::Keycode key) {
|
||||||
|
return _valid(key) ? _policies[key]._session : 0; }
|
||||||
|
|
||||||
|
void apply_config(Session_list &session_list);
|
||||||
|
|
||||||
|
bool is_operation_key(Input::Keycode key) const {
|
||||||
|
return _valid(key) && (_policies[key].xray() || _policies[key].kill()); }
|
||||||
|
|
||||||
|
bool is_xray_key(Input::Keycode key) const {
|
||||||
|
return _valid(key) && _policies[key].xray(); }
|
||||||
|
|
||||||
|
bool is_kill_key(Input::Keycode key) const {
|
||||||
|
return _valid(key) && _policies[key].kill(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* _GLOBAL_KEYS_H_ */
|
@ -14,13 +14,21 @@
|
|||||||
#ifndef _SESSION_H_
|
#ifndef _SESSION_H_
|
||||||
#define _SESSION_H_
|
#define _SESSION_H_
|
||||||
|
|
||||||
|
/* Genode includes */
|
||||||
|
#include <util/list.h>
|
||||||
|
|
||||||
|
/* local includes */
|
||||||
#include "string.h"
|
#include "string.h"
|
||||||
|
|
||||||
class Texture;
|
class Texture;
|
||||||
class View;
|
class View;
|
||||||
|
class Session;
|
||||||
|
|
||||||
namespace Input { class Event; }
|
namespace Input { class Event; }
|
||||||
|
|
||||||
class Session
|
typedef Genode::List<Session> Session_list;
|
||||||
|
|
||||||
|
class Session : public Session_list::Element
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -23,11 +23,17 @@
|
|||||||
#include "mode.h"
|
#include "mode.h"
|
||||||
#include "menubar.h"
|
#include "menubar.h"
|
||||||
#include "view_stack.h"
|
#include "view_stack.h"
|
||||||
|
#include "global_keys.h"
|
||||||
|
|
||||||
class User_state : public Mode, public View_stack
|
class User_state : public Mode, public View_stack
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Policy for the routing of global keys
|
||||||
|
*/
|
||||||
|
Global_keys &_global_keys;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Number of currently pressed keys.
|
* Number of currently pressed keys.
|
||||||
* This counter is used to determine if the user
|
* This counter is used to determine if the user
|
||||||
@ -52,12 +58,22 @@ class User_state : public Mode, public View_stack
|
|||||||
*/
|
*/
|
||||||
View *_pointed_view;
|
View *_pointed_view;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Session that receives the current stream of input events
|
||||||
|
*/
|
||||||
|
Session *_input_receiver;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* True while a global key sequence is processed
|
||||||
|
*/
|
||||||
|
bool _global_key_sequence;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
*/
|
*/
|
||||||
User_state(Canvas *canvas, Menubar *menubar);
|
User_state(Global_keys &global_keys, Canvas *canvas, Menubar *menubar);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle input event
|
* Handle input event
|
||||||
|
Reference in New Issue
Block a user