Compare commits
2 Commits
31cd35bf84
...
e7a0945a93
| Author | SHA1 | Date | |
|---|---|---|---|
|
e7a0945a93
|
|||
|
47bd59b806
|
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include "backend/output_utils.h"
|
#include "backend/output_utils.h"
|
||||||
#include "backend/x11/cursor.h"
|
#include "backend/x11/cursor.h"
|
||||||
|
#include "backend/x11/tray.h"
|
||||||
#include "backend/x11/window.h"
|
#include "backend/x11/window.h"
|
||||||
#include "base/app.h"
|
#include "base/app.h"
|
||||||
#include "base/array.h"
|
#include "base/array.h"
|
||||||
@@ -845,6 +846,8 @@ backend_bar_window_t backend_create_bar_window(
|
|||||||
fatal("cannot create cairo context: %s", cairo_status_to_string(statue));
|
fatal("cannot create cairo context: %s", cairo_status_to_string(statue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (enable_tray) tray_init(backend, window_id, width, height);
|
||||||
|
|
||||||
return (backend_bar_window_t){
|
return (backend_bar_window_t){
|
||||||
.window_id = window_id,
|
.window_id = window_id,
|
||||||
.cr = cr,
|
.cr = cr,
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
#include "common/window.h"
|
#include "common/window.h"
|
||||||
#include "interface/event.h"
|
#include "interface/event.h"
|
||||||
#include "interface/tray.h"
|
|
||||||
|
|
||||||
#define EWMH_ATOMS(X) \
|
#define EWMH_ATOMS(X) \
|
||||||
X(_NET_WM_NAME) \
|
X(_NET_WM_NAME) \
|
||||||
@@ -47,6 +46,8 @@
|
|||||||
X(_NET_WM_WINDOW_TYPE_NOTIFICATION)
|
X(_NET_WM_WINDOW_TYPE_NOTIFICATION)
|
||||||
|
|
||||||
#define ATOM_LIST(X) \
|
#define ATOM_LIST(X) \
|
||||||
|
X(MANAGER) \
|
||||||
|
\
|
||||||
X(COMPOUND_TEXT) \
|
X(COMPOUND_TEXT) \
|
||||||
X(UTF8_STRING) \
|
X(UTF8_STRING) \
|
||||||
\
|
\
|
||||||
@@ -87,17 +88,7 @@ typedef enum cursor_t {
|
|||||||
ZDWM_CURSOR_COUNT,
|
ZDWM_CURSOR_COUNT,
|
||||||
} cursor_t;
|
} cursor_t;
|
||||||
|
|
||||||
typedef struct tray_host_t {
|
typedef struct tray_host_t tray_host_t;
|
||||||
bool enabled;
|
|
||||||
xcb_window_t host_window;
|
|
||||||
xcb_window_t container;
|
|
||||||
int32_t icon_size;
|
|
||||||
size_t icon_count;
|
|
||||||
tray_icons_change_cb_t icon_change_cb;
|
|
||||||
void *cb_user_data;
|
|
||||||
} tray_host_t;
|
|
||||||
|
|
||||||
extern const tray_api_t tray_api;
|
|
||||||
|
|
||||||
typedef struct backend_t {
|
typedef struct backend_t {
|
||||||
xcb_key_symbols_t *key_symbols;
|
xcb_key_symbols_t *key_symbols;
|
||||||
@@ -123,7 +114,7 @@ typedef struct backend_t {
|
|||||||
window_list_t map;
|
window_list_t map;
|
||||||
window_list_t kill;
|
window_list_t kill;
|
||||||
|
|
||||||
tray_host_t tray;
|
tray_host_t *tray;
|
||||||
} backend_t;
|
} backend_t;
|
||||||
|
|
||||||
bool populate_window_event(
|
bool populate_window_event(
|
||||||
|
|||||||
@@ -1,41 +1,66 @@
|
|||||||
#include "interface/tray.h"
|
#include "backend/x11/tray.h"
|
||||||
|
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <xcb/xproto.h>
|
||||||
#include <zdwm/types.h>
|
#include <zdwm/types.h>
|
||||||
|
|
||||||
|
#include "backend/x11/window.h"
|
||||||
|
#include "base/log.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "base/memory.h"
|
||||||
|
#include "interface/tray.h"
|
||||||
#include "interface/types.h"
|
#include "interface/types.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
static window_id_t tray_host_window(void *handle) {
|
typedef struct tray_host_t {
|
||||||
backend_t *backend = handle;
|
bool enabled;
|
||||||
if (backend == nullptr || !backend->tray.enabled) {
|
bool initialized;
|
||||||
return ZDWM_WINDOW_ID_INVALID;
|
xcb_atom_t selection_atom;
|
||||||
}
|
xcb_window_t host_window;
|
||||||
|
xcb_window_t container;
|
||||||
|
int32_t icon_size;
|
||||||
|
size_t icon_count;
|
||||||
|
tray_icons_change_cb_t icon_change_cb;
|
||||||
|
void *cb_user_data;
|
||||||
|
} tray_host_t;
|
||||||
|
|
||||||
return backend->tray.host_window;
|
static tray_host_t *get_tray_host(void *handle) {
|
||||||
|
backend_t *backend = handle;
|
||||||
|
if (!backend) return nullptr;
|
||||||
|
|
||||||
|
auto tray = backend->tray;
|
||||||
|
if (!tray || !tray->enabled) return nullptr;
|
||||||
|
|
||||||
|
return tray;
|
||||||
|
}
|
||||||
|
|
||||||
|
static window_id_t tray_host_window(void *handle) {
|
||||||
|
auto tray = get_tray_host(handle);
|
||||||
|
if (!tray) return ZDWM_WINDOW_ID_INVALID;
|
||||||
|
|
||||||
|
return tray->host_window;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t tray_icon_count(void *handle) {
|
static size_t tray_icon_count(void *handle) {
|
||||||
backend_t *backend = handle;
|
auto tray = get_tray_host(handle);
|
||||||
if (backend == nullptr || !backend->tray.enabled) return 0;
|
|
||||||
|
|
||||||
return backend->tray.icon_count;
|
return tray ? tray->icon_count : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t tray_icon_size(void *handle) {
|
static int32_t tray_icon_size(void *handle) {
|
||||||
backend_t *backend = handle;
|
auto tray = get_tray_host(handle);
|
||||||
if (backend == nullptr || !backend->tray.enabled) return 0;
|
|
||||||
|
|
||||||
return backend->tray.icon_size;
|
return tray ? tray->icon_size : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void tray_place(void *handle, int32_t x) {
|
static void tray_place(void *handle, int32_t x) {
|
||||||
backend_t *backend = handle;
|
auto tray = get_tray_host(handle);
|
||||||
if (backend == nullptr || !backend->tray.enabled) return;
|
if (!tray) return;
|
||||||
|
|
||||||
auto conn = backend->conn;
|
auto conn = ((backend_t *)handle)->conn;
|
||||||
auto tray = &backend->tray;
|
|
||||||
uint16_t value_mask = XCB_CONFIG_WINDOW_X;
|
uint16_t value_mask = XCB_CONFIG_WINDOW_X;
|
||||||
xcb_configure_window_value_list_t value_list = {.x = x};
|
xcb_configure_window_value_list_t value_list = {.x = x};
|
||||||
xcb_configure_window_aux(conn, tray->container, value_mask, &value_list);
|
xcb_configure_window_aux(conn, tray->container, value_mask, &value_list);
|
||||||
@@ -43,11 +68,11 @@ static void tray_place(void *handle, int32_t x) {
|
|||||||
|
|
||||||
static void
|
static void
|
||||||
tray_set_listeners(void *handle, tray_icons_change_cb_t cb, void *user_data) {
|
tray_set_listeners(void *handle, tray_icons_change_cb_t cb, void *user_data) {
|
||||||
backend_t *backend = handle;
|
auto tray = get_tray_host(handle);
|
||||||
if (backend == nullptr) return;
|
if (!tray) return;
|
||||||
|
|
||||||
backend->tray.icon_change_cb = cb;
|
tray->icon_change_cb = cb;
|
||||||
backend->tray.cb_user_data = user_data;
|
tray->cb_user_data = user_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
const tray_api_t tray_api = {
|
const tray_api_t tray_api = {
|
||||||
@@ -57,3 +82,164 @@ const tray_api_t tray_api = {
|
|||||||
.place = tray_place,
|
.place = tray_place,
|
||||||
.set_listener = tray_set_listeners,
|
.set_listener = tray_set_listeners,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool tray_intern_selection_atom(tray_host_t *tray, backend_t *backend) {
|
||||||
|
auto conn = backend->conn;
|
||||||
|
auto screenp = backend->screenp;
|
||||||
|
|
||||||
|
char selection_name[64] = {0};
|
||||||
|
auto atom_name_format = "_NET_SYSTEM_TRAY_S%d";
|
||||||
|
snprintf(selection_name, sizeof(selection_name), atom_name_format, screenp);
|
||||||
|
|
||||||
|
uint16_t name_length = strlen(selection_name);
|
||||||
|
auto cookie = xcb_intern_atom(conn, false, name_length, selection_name);
|
||||||
|
auto reply = xcb_intern_atom_reply(conn, cookie, nullptr);
|
||||||
|
if (!reply) {
|
||||||
|
warn("cannot intern tray selection atom: %s", selection_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tray->selection_atom = reply->atom;
|
||||||
|
p_delete(&reply);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct tray_host_window_t {
|
||||||
|
xcb_window_t window;
|
||||||
|
int32_t width;
|
||||||
|
int32_t height;
|
||||||
|
} tray_host_window_t;
|
||||||
|
|
||||||
|
static bool tray_create_container_window(
|
||||||
|
tray_host_t *tray,
|
||||||
|
backend_t *backend,
|
||||||
|
tray_host_window_t host_window
|
||||||
|
) {
|
||||||
|
auto conn = backend->conn;
|
||||||
|
auto window_id = xcb_generate_id(conn);
|
||||||
|
|
||||||
|
xcb_create_window_value_list_t value_list = {
|
||||||
|
.override_redirect = true,
|
||||||
|
.event_mask =
|
||||||
|
XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY | XCB_EVENT_MASK_STRUCTURE_NOTIFY
|
||||||
|
};
|
||||||
|
auto cookie = xcb_create_window_aux_checked(
|
||||||
|
conn,
|
||||||
|
XCB_COPY_FROM_PARENT,
|
||||||
|
window_id,
|
||||||
|
host_window.window,
|
||||||
|
(int16_t)host_window.width,
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
(uint16_t)MAX(1, host_window.height),
|
||||||
|
0,
|
||||||
|
XCB_WINDOW_CLASS_INPUT_OUTPUT,
|
||||||
|
XCB_COPY_FROM_PARENT,
|
||||||
|
XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK,
|
||||||
|
&value_list
|
||||||
|
);
|
||||||
|
|
||||||
|
if (xcb_request_check(conn, cookie)) {
|
||||||
|
warn("cannot create system tray container window: 0x%x", window_id);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tray->container = window_id;
|
||||||
|
|
||||||
|
window_set_class_instance(conn, window_id);
|
||||||
|
|
||||||
|
#define TRAY_CONTAINER_WINDOW_NAME APP_NAME "_tray"
|
||||||
|
window_set_name_static(conn, window_id, TRAY_CONTAINER_WINDOW_NAME);
|
||||||
|
auto name = TRAY_CONTAINER_WINDOW_NAME;
|
||||||
|
auto name_len = sizeof(TRAY_CONTAINER_WINDOW_NAME) - 1;
|
||||||
|
#undef TRAY_CONTAINER_WINDOW_NAME
|
||||||
|
|
||||||
|
uint8_t mode = XCB_PROP_MODE_REPLACE;
|
||||||
|
auto atoms = &backend->atoms;
|
||||||
|
auto prop = atoms->_NET_WM_NAME;
|
||||||
|
auto type = atoms->UTF8_STRING;
|
||||||
|
xcb_change_property(conn, mode, window_id, prop, type, 8, name_len, name);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool tray_take_selection_owner(tray_host_t *tray, backend_t *backend) {
|
||||||
|
auto conn = backend->conn;
|
||||||
|
auto atom = tray->selection_atom;
|
||||||
|
auto cookie = xcb_get_selection_owner(conn, atom);
|
||||||
|
auto reply = xcb_get_selection_owner_reply(conn, cookie, nullptr);
|
||||||
|
if (reply && reply->owner != XCB_WINDOW_NONE) {
|
||||||
|
warn(
|
||||||
|
"system tray selection is already owned by window: 0x%x",
|
||||||
|
reply->owner
|
||||||
|
);
|
||||||
|
p_delete(&reply);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_delete(&reply);
|
||||||
|
|
||||||
|
auto owner = tray->container;
|
||||||
|
xcb_set_selection_owner(conn, owner, atom, XCB_CURRENT_TIME);
|
||||||
|
|
||||||
|
cookie = xcb_get_selection_owner(conn, atom);
|
||||||
|
reply = xcb_get_selection_owner_reply(conn, cookie, nullptr);
|
||||||
|
bool success = reply && reply->owner == owner;
|
||||||
|
p_delete(&reply);
|
||||||
|
|
||||||
|
if (!success) warn("cannot acquire system tray selection");
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void tray_broadcast_manager(tray_host_t *tray, backend_t *backend) {
|
||||||
|
auto conn = backend->conn;
|
||||||
|
auto root = backend->screen->root;
|
||||||
|
|
||||||
|
xcb_client_message_event_t event = {
|
||||||
|
.response_type = XCB_CLIENT_MESSAGE,
|
||||||
|
.window = root,
|
||||||
|
.format = 32,
|
||||||
|
.type = backend->atoms.MANAGER,
|
||||||
|
.data.data32 = {
|
||||||
|
[0] = XCB_CURRENT_TIME,
|
||||||
|
[1] = tray->selection_atom,
|
||||||
|
[2] = tray->container,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
uint32_t event_mask = XCB_EVENT_MASK_STRUCTURE_NOTIFY;
|
||||||
|
xcb_send_event(conn, false, root, event_mask, (char *)&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tray_init(
|
||||||
|
backend_t *backend,
|
||||||
|
xcb_window_t host_window,
|
||||||
|
int32_t host_width,
|
||||||
|
int32_t host_height
|
||||||
|
) {
|
||||||
|
auto tray = get_tray_host(backend);
|
||||||
|
if (!tray) {
|
||||||
|
tray = p_new(tray_host_t, 1);
|
||||||
|
backend->tray = tray;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tray->initialized) return;
|
||||||
|
|
||||||
|
if (!tray_intern_selection_atom(tray, backend)) return;
|
||||||
|
|
||||||
|
tray_host_window_t host_window_params = {
|
||||||
|
.window = host_window,
|
||||||
|
.width = host_width,
|
||||||
|
.height = host_height
|
||||||
|
};
|
||||||
|
if (!tray_create_container_window(tray, backend, host_window_params)) return;
|
||||||
|
if (!tray_take_selection_owner(tray, backend)) {
|
||||||
|
/* TODO: cleanup tray */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tray->enabled = true;
|
||||||
|
tray->initialized = true;
|
||||||
|
|
||||||
|
tray_broadcast_manager(tray, backend);
|
||||||
|
xcb_flush(backend->conn);
|
||||||
|
}
|
||||||
|
|||||||
17
src/backend/x11/tray.h
Normal file
17
src/backend/x11/tray.h
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
|
#include "interface/tray.h"
|
||||||
|
|
||||||
|
extern const tray_api_t tray_api;
|
||||||
|
|
||||||
|
typedef struct backend_t backend_t;
|
||||||
|
|
||||||
|
void tray_init(
|
||||||
|
backend_t *backend,
|
||||||
|
xcb_window_t host_window,
|
||||||
|
int32_t host_width,
|
||||||
|
int32_t host_height
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user