目錄
前言
1 weston架構下輸入管理流程
2 按鍵适配處理分析
3 亮滅屏分析
3.1 destop層面下的分析
3.2 compositor層面下的分析
4 總結
前言
在weston架構下,支援的輸入裝置通常是标準裝置,如:鍵盤、滑鼠、觸摸屏等。若要支援我們自定義的鍵盤,需要在weston架構下做适配。
1 weston架構下輸入管理流程
該部分我們參考ariesjzj大神的《輸入管理》章節部分,了解了第一部分之後,基于該部分在第二章中闡述适配的過程。
Weston中的輸入管理子產品與libinput對接,它實作了兩大部分的功能:一是對輸入裝置的維護,二是對輸入事件的處理。對于輸入事件既會在Weston中做處理,也會傳給相應的client。從事件處理模型上來看,libinput的主循環監聽udev monitor fd,它主要用于監聽裝置的添加删除事件。如果有裝置添加,會打開該裝置并把fd加入到libinput的主循環上。另一方面,Weston中會将libinput的epoll fd加入主循環。這樣形成級聯的epoll,無論是udev monitor還是input device的fd有事件來,都會通知到Weston和libinput的主循環。這些事件通過libinput中的事件緩沖隊列存儲,而Weston會作為消費者從中拿事件并根據事件類型進行處理。
2 按鍵适配處理分析
weston在嵌入式裝置中運作,常采用的指令為:weston --tty=1 --backend=drm-backend.so ,後端采取的是drm對接顯示硬體。drm-backend.so來自于weston/libweston/compositor_drm.c
drm_backend_create
udev_input_init -> process_events(input);
-> udev_input_enable
wl_event_loop_add_fd 将input裝置添加到epoll中去監控
在udev_input_enable 中将輸入裝置的fd加入到epoll循環中,如果該裝置信号可讀,則觸發回調函數:libinput_source_dispatch
libinput_source_dispatch
udev_input_dispatch
process_events(input)
第二個epoll循環:
while ((event = libinput_get_event(input->libinput))) {
process_event(event);
libinput_event_destroy(event);
}
在process_event中處理了udev和evdev事件:
process_event(struct libinput_event *event)
{
if (udev_input_process_event(event)) //裝置add和remove
return;
if (evdev_device_process_event(event)) //裝置資料讀取處理
return;
}
evdev_device_process_event中處理了按鍵、滑鼠、觸摸事件,以按鍵處理為例:
evdev_device_process_event
handle_keyboard_key //按鍵事件處理
notify_key
在notify_key中可以分析出,按鍵按下之後,螢幕就會被喚醒。如果在此處進行按鍵鍵值替換,就可以實作自定義的按鍵亮屏。
weston_log("key is: %d",key);
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
weston_compositor_idle_inhibit(compositor);
} else {
weston_compositor_idle_release(compositor);
}
3 亮滅屏分析
weston架構下實作亮滅屏應該從使用者層面分析,而非底層。比如使用者層面的滅屏步驟中包含:LCD螢幕下電、焦點失能、上鎖等一系列的操作,若隻是實作了簡單的LCD螢幕下電并不能真正的實作滅屏操作。
3.1 destop層面下的分析
那麼首先我們從使用者層面分析亮滅屏的操作,在/weston/desktop-shell/shell.c中,通過wet_shell_init初始化了亮滅屏的操作。如下所示:
shell->idle_listener.notify = idle_handler;//熄屏信号的回調函數
wl_signal_add(&ec->idle_signal, &shell->idle_listener);
shell->wake_listener.notify = wake_handler;//亮屏信号的回調函數
wl_signal_add(&ec->wake_signal, &shell->wake_listener);
idle_handler的實作為:
static void
idle_handler(struct wl_listener *listener, void *data)
{
struct desktop_shell *shell =
container_of(listener, struct desktop_shell, idle_listener);
struct weston_seat *seat;
wl_list_for_each(seat, &shell->compositor->seat_list, link)
weston_seat_break_desktop_grabs(seat);
shell_fade(shell, FADE_OUT);
/* lock() is called from shell_fade_done_for_output() */
}
其中shell_fade最終實作了lock操作
shell_fade
shell_fade_done_for_output
lock(shell);
lock中實作的操作
static void
lock(struct desktop_shell *shell)
{
struct workspace *ws = get_current_workspace(shell);
if (shell->locked) {
weston_compositor_sleep(shell->compositor);
return;
}
shell->locked = true;
/* Hide all surfaces by removing the fullscreen, panel and
* toplevel layers. This way nothing else can show or receive
* input events while we are locked. */
weston_layer_unset_position(&shell->panel_layer); //移除panel
weston_layer_unset_position(&shell->fullscreen_layer); //移除fullscreen
if (shell->showing_input_panels)
weston_layer_unset_position(&shell->input_panel_layer); //不會接收到輸入事件
weston_layer_unset_position(&ws->layer);
weston_layer_set_position(&shell->lock_layer, //鎖屏
WESTON_LAYER_POSITION_LOCK);
weston_compositor_sleep(shell->compositor); //進入睡眠狀态
/* Remove the keyboard focus on all seats. This will be
* restored to the workspace's saved state via
* restore_focus_state when the compositor is unlocked */
unfocus_all_seats(shell); //焦點使能
/* TODO: disable bindings that should not work while locked. */
/* All this must be undone in resume_desktop(). */
}
是以上述的lock才是真正的實作了滅屏的一系列操作。
3.2 compositor層面下的分析
首先分析weston/compositor/main.c函數
weston_compositor_create
wl_event_loop_add_timer(loop, idle_handler, ec);
使用wl_event_loop_add_timer建立了一個定時器事件,定時器觸發compositor中的idle_handler。
idle_handler的實作為:
static int
idle_handler(void *data)
{
struct weston_compositor *compositor = data;
if (compositor->idle_inhibit)
return 1;
compositor->state = WESTON_COMPOSITOR_IDLE;
wl_signal_emit(&compositor->idle_signal, compositor); //發送idle_signal信号
return 1;
}
所示wl_signal_emit發送WESTON_COMPOSITOR_IDLE信号,3.1節中的desktop-shell收到信号之後就執行完整的滅屏操作。
繼續分析main.c中的内容,其中有一段:
if (idle_time < 0)
weston_config_section_get_int(section, "idle-time", &idle_time, -1);
if (idle_time < 0)
idle_time = 300; /* default idle timeout, in seconds */
ec->idle_time = idle_time;
ec->default_pointer_grab = NULL;
ec->exit = handle_exit;
.
.
.
weston_compositor_wake(ec);
該事件作用是什麼呢?如果你對weston有比較深的了解,就會知道在weston預設滅屏事件為5分鐘。上述的代碼就是為了實作所述的功能。
首先設定了一個idle_time事件300,然後傳遞到weston_compositor_wake(ec)中去,weston_compositor_wake中的内容:
WL_EXPORT void
weston_compositor_wake(struct weston_compositor *compositor)
{
uint32_t old_state = compositor->state;
/* The state needs to be changed before emitting the wake
* signal because that may try to schedule a repaint which
* will not work if the compositor is still sleeping */
compositor->state = WESTON_COMPOSITOR_ACTIVE;
switch (old_state) { //開始時候,如果處于熄屏的各種狀态,則喚醒;因為剛開機不需要滅屏
case WESTON_COMPOSITOR_SLEEPING:
case WESTON_COMPOSITOR_IDLE:
case WESTON_COMPOSITOR_OFFSCREEN:
weston_compositor_dpms(compositor, WESTON_DPMS_ON);
wl_signal_emit(&compositor->wake_signal, compositor);
/* fall through */
default: //開機之後,開啟5分鐘的定時滅屏操作
wl_event_source_timer_update(compositor->idle_source,
compositor->idle_time * 1000);
}
}
4 總結
如果需要滅屏,則狀态為WESTON_COMPOSITOR_SLEEPING;WESTON_COMPOSITOR_IDLE;WESTON_COMPOSITOR_OFFSCREEN;執行
compositor->state = WESTON_COMPOSITOR_IDLE;
wl_signal_emit(&compositor->idle_signal, compositor);
如果需要亮屏,則執行喚醒操作:
weston_compositor_wake(ec);
自定義輸入按鍵實作過程是一種 "自底向上" 的過程,上層destop-shell需要亮滅屏的操作是由底層的按鍵觸發實作的。