mirror of
https://github.com/lbonn/rofi
synced 2024-11-12 23:27:15 +00:00
ccea31234a
Happens when destroy() is called without previous init()
635 lines
20 KiB
C
635 lines
20 KiB
C
/*
|
|
* rofi
|
|
*
|
|
* MIT/X11 License
|
|
* Copyright © 2013-2022 Qball Cow <qball@gmpclient.org>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining
|
|
* a copy of this software and associated documentation files (the
|
|
* "Software"), to deal in the Software without restriction, including
|
|
* without limitation the rights to use, copy, modify, merge, publish,
|
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
|
* permit persons to whom the Software is furnished to do so, subject to
|
|
* the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
/** The log domain of this dialog. */
|
|
#define G_LOG_DOMAIN "Modes.Window"
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef WINDOW_MODE
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <glib.h>
|
|
#include <wayland-client.h>
|
|
|
|
#include "display.h"
|
|
#include "helper.h"
|
|
#include "modes/wayland-window.h"
|
|
#include "rofi.h"
|
|
#include "settings.h"
|
|
#include "wayland-internal.h"
|
|
#include "widgets/textbox.h"
|
|
|
|
#include "mode-private.h"
|
|
#include "rofi-icon-fetcher.h"
|
|
|
|
#include "wlr-foreign-toplevel-management-unstable-v1-protocol.h"
|
|
|
|
#define WLR_FOREIGN_TOPLEVEL_VERSION 3
|
|
|
|
enum WaylandWindowMatchingFields {
|
|
WW_MATCH_FIELD_TITLE = 1 << 0,
|
|
WW_MATCH_FIELD_APP_ID = 1 << 1,
|
|
WW_MATCH_FIELD_ALL = ~0,
|
|
};
|
|
|
|
typedef struct _WaylandWindowModePrivateData {
|
|
wayland_stuff *wayland;
|
|
struct wl_registry *registry;
|
|
struct zwlr_foreign_toplevel_manager_v1 *manager;
|
|
GList *toplevels; /* List of ForeignToplevelHandle */
|
|
|
|
/* initial rendering complete, updates allowed */
|
|
gboolean visible;
|
|
int match_fields; /* bitmask of WaylandWindowMatchingFields */
|
|
glong title_len;
|
|
glong app_id_len;
|
|
GRegex *window_regex;
|
|
} WaylandWindowModePrivateData;
|
|
|
|
enum ForeignToplevelState {
|
|
TOPLEVEL_STATE_MAXIMIZED = 1
|
|
<< ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MAXIMIZED,
|
|
TOPLEVEL_STATE_MINIMIZED = 1
|
|
<< ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_MINIMIZED,
|
|
TOPLEVEL_STATE_ACTIVATED = 1
|
|
<< ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_ACTIVATED,
|
|
TOPLEVEL_STATE_FULLSCREEN =
|
|
1 << ZWLR_FOREIGN_TOPLEVEL_HANDLE_V1_STATE_FULLSCREEN,
|
|
TOPLEVEL_STATE_CLOSED = 1 << 4
|
|
};
|
|
|
|
typedef struct {
|
|
struct zwlr_foreign_toplevel_handle_v1 *handle;
|
|
WaylandWindowModePrivateData *view;
|
|
|
|
gchar *app_id;
|
|
glong app_id_len;
|
|
gchar *title;
|
|
glong title_len;
|
|
int state;
|
|
|
|
unsigned int cached_icon_uid;
|
|
unsigned int cached_icon_size;
|
|
guint cached_icon_scale;
|
|
} ForeignToplevelHandle;
|
|
|
|
static void foreign_toplevel_handle_free(ForeignToplevelHandle *self) {
|
|
|
|
if (self->handle) {
|
|
zwlr_foreign_toplevel_handle_v1_destroy(self->handle);
|
|
self->handle = NULL;
|
|
}
|
|
g_free(self->title);
|
|
g_free(self->app_id);
|
|
g_free(self);
|
|
}
|
|
|
|
static void toplevels_list_update_max_len(gpointer data, gpointer user_data) {
|
|
WaylandWindowModePrivateData *pd = (WaylandWindowModePrivateData *)user_data;
|
|
ForeignToplevelHandle *entry = (ForeignToplevelHandle *)data;
|
|
|
|
pd->title_len = MAX(entry->title_len, pd->title_len);
|
|
pd->app_id_len = MAX(entry->app_id_len, pd->app_id_len);
|
|
}
|
|
|
|
/* Update column alignment and schedule reload */
|
|
static void wayland_window_update_toplevel(ForeignToplevelHandle *toplevel) {
|
|
WaylandWindowModePrivateData *pd = toplevel->view;
|
|
|
|
if (!pd->visible) {
|
|
/* initial fetch, just add the current item */
|
|
toplevels_list_update_max_len(toplevel, pd);
|
|
} else {
|
|
/* async update, recalculate from scratch */
|
|
pd->title_len = 0;
|
|
pd->app_id_len = 0;
|
|
g_list_foreach(pd->toplevels, toplevels_list_update_max_len, pd);
|
|
rofi_view_reload();
|
|
}
|
|
}
|
|
|
|
/* requests */
|
|
|
|
static void foreign_toplevel_handle_activate(ForeignToplevelHandle *self,
|
|
struct wl_seat *seat) {
|
|
zwlr_foreign_toplevel_handle_v1_activate(self->handle, seat);
|
|
}
|
|
|
|
static void foreign_toplevel_handle_close(ForeignToplevelHandle *self) {
|
|
zwlr_foreign_toplevel_handle_v1_close(self->handle);
|
|
}
|
|
|
|
/* events */
|
|
|
|
static void foreign_toplevel_handle_title(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
const char *title) {
|
|
ForeignToplevelHandle *self = (ForeignToplevelHandle *)data;
|
|
if (self->title) {
|
|
g_free(self->title);
|
|
}
|
|
self->title = g_strdup(title);
|
|
self->title_len = g_utf8_strlen(self->title, -1);
|
|
}
|
|
|
|
static void foreign_toplevel_handle_app_id(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
const char *app_id) {
|
|
ForeignToplevelHandle *self = (ForeignToplevelHandle *)data;
|
|
if (self->app_id) {
|
|
g_free(self->app_id);
|
|
}
|
|
self->app_id = g_strdup(app_id);
|
|
self->app_id_len = g_utf8_strlen(self->app_id, -1);
|
|
}
|
|
|
|
static void foreign_toplevel_handle_output_enter(
|
|
G_GNUC_UNUSED void *data,
|
|
G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
G_GNUC_UNUSED struct wl_output *output) {
|
|
/* ignore */
|
|
}
|
|
|
|
static void foreign_toplevel_handle_output_leave(
|
|
G_GNUC_UNUSED void *data,
|
|
G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
G_GNUC_UNUSED struct wl_output *output) {
|
|
/* ignore */
|
|
}
|
|
|
|
static void foreign_toplevel_handle_state(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
struct wl_array *value) {
|
|
ForeignToplevelHandle *self = (ForeignToplevelHandle *)data;
|
|
uint32_t *elem;
|
|
|
|
self->state = 0;
|
|
wl_array_for_each(elem, value) { self->state |= 1 << *elem; }
|
|
}
|
|
|
|
static void foreign_toplevel_handle_done(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
|
ForeignToplevelHandle *self = (ForeignToplevelHandle *)data;
|
|
|
|
g_debug("window %p id=%s title=%s state=%d\n", (void *)self, self->app_id,
|
|
self->title, self->state);
|
|
|
|
wayland_window_update_toplevel(self);
|
|
}
|
|
|
|
static void foreign_toplevel_handle_closed(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle) {
|
|
ForeignToplevelHandle *self = (ForeignToplevelHandle *)data;
|
|
|
|
/* the handle is inert and will receive no further events */
|
|
self->state = TOPLEVEL_STATE_CLOSED;
|
|
self->view->toplevels = g_list_remove(self->view->toplevels, self);
|
|
wayland_window_update_toplevel(self);
|
|
foreign_toplevel_handle_free(self);
|
|
}
|
|
|
|
static void foreign_toplevel_handle_parent(
|
|
G_GNUC_UNUSED void *data,
|
|
G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
G_GNUC_UNUSED struct zwlr_foreign_toplevel_handle_v1 *parent) {
|
|
/* ignore */
|
|
}
|
|
|
|
static struct zwlr_foreign_toplevel_handle_v1_listener
|
|
foreign_toplevel_handle_listener = {
|
|
.title = &foreign_toplevel_handle_title,
|
|
.app_id = &foreign_toplevel_handle_app_id,
|
|
.output_enter = &foreign_toplevel_handle_output_enter,
|
|
.output_leave = &foreign_toplevel_handle_output_leave,
|
|
.state = &foreign_toplevel_handle_state,
|
|
.done = &foreign_toplevel_handle_done,
|
|
.closed = &foreign_toplevel_handle_closed,
|
|
.parent = &foreign_toplevel_handle_parent};
|
|
|
|
static ForeignToplevelHandle *
|
|
foreign_toplevel_handle_new(struct zwlr_foreign_toplevel_handle_v1 *handle,
|
|
WaylandWindowModePrivateData *view) {
|
|
ForeignToplevelHandle *self =
|
|
(ForeignToplevelHandle *)g_malloc0(sizeof(ForeignToplevelHandle));
|
|
|
|
self->handle = handle;
|
|
self->view = view;
|
|
zwlr_foreign_toplevel_handle_v1_add_listener(
|
|
handle, &foreign_toplevel_handle_listener, self);
|
|
return self;
|
|
}
|
|
|
|
static void foreign_toplevel_manager_toplevel(
|
|
void *data, G_GNUC_UNUSED struct zwlr_foreign_toplevel_manager_v1 *manager,
|
|
struct zwlr_foreign_toplevel_handle_v1 *toplevel) {
|
|
WaylandWindowModePrivateData *pd = (WaylandWindowModePrivateData *)data;
|
|
|
|
ForeignToplevelHandle *handle = foreign_toplevel_handle_new(toplevel, pd);
|
|
pd->toplevels = g_list_prepend(pd->toplevels, handle);
|
|
}
|
|
|
|
static void foreign_toplevel_manager_finished(
|
|
G_GNUC_UNUSED void *data,
|
|
struct zwlr_foreign_toplevel_manager_v1 *manager) {
|
|
zwlr_foreign_toplevel_manager_v1_destroy(manager);
|
|
}
|
|
|
|
static struct zwlr_foreign_toplevel_manager_v1_listener
|
|
foreign_toplevel_manager_listener = {
|
|
.toplevel = &foreign_toplevel_manager_toplevel,
|
|
.finished = &foreign_toplevel_manager_finished};
|
|
|
|
static void handle_global(void *data, struct wl_registry *registry,
|
|
uint32_t name, const char *interface,
|
|
uint32_t version) {
|
|
WaylandWindowModePrivateData *pd = (WaylandWindowModePrivateData *)data;
|
|
|
|
if (g_strcmp0(interface, zwlr_foreign_toplevel_manager_v1_interface.name) ==
|
|
0) {
|
|
|
|
pd->manager = (struct zwlr_foreign_toplevel_manager_v1 *)wl_registry_bind(
|
|
registry, name, &zwlr_foreign_toplevel_manager_v1_interface,
|
|
MIN(version, WLR_FOREIGN_TOPLEVEL_VERSION));
|
|
}
|
|
}
|
|
|
|
static void handle_global_remove(G_GNUC_UNUSED void *data,
|
|
G_GNUC_UNUSED struct wl_registry *registry,
|
|
G_GNUC_UNUSED uint32_t name) {}
|
|
|
|
static struct wl_registry_listener registry_listener = {
|
|
.global = &handle_global, .global_remove = &handle_global_remove};
|
|
|
|
static int wayland_window_mode_parse_fields(void) {
|
|
int result = 0;
|
|
char *savept = NULL;
|
|
// Make a copy, as strtok will modify it.
|
|
char *switcher_str = g_strdup(config.window_match_fields);
|
|
|
|
const char *const sep = ",#";
|
|
for (char *token = strtok_r(switcher_str, sep, &savept); token != NULL;
|
|
token = strtok_r(NULL, sep, &savept)) {
|
|
if (g_strcmp0(token, "all") == 0) {
|
|
result |= WW_MATCH_FIELD_ALL;
|
|
|
|
} else if (g_strcmp0(token, "title") == 0) {
|
|
result |= WW_MATCH_FIELD_TITLE;
|
|
|
|
} else if (g_strcmp0(token, "class") == 0 ||
|
|
g_strcmp0(token, "app-id") == 0) {
|
|
result |= WW_MATCH_FIELD_APP_ID;
|
|
|
|
} else {
|
|
g_warning("Unsupported window field name :%s. "
|
|
"Wayland window switcher supports only 'title' and 'app-id' "
|
|
"('class') fields",
|
|
token);
|
|
}
|
|
}
|
|
g_free(switcher_str);
|
|
return result;
|
|
}
|
|
|
|
static void get_wayland_window(Mode *sw) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
|
|
pd->match_fields = wayland_window_mode_parse_fields();
|
|
pd->window_regex = g_regex_new("{[-\\w]+(:-?[0-9]+)?}", 0, 0, NULL);
|
|
|
|
pd->wayland = wayland;
|
|
|
|
pd->registry = wl_display_get_registry(wayland->display);
|
|
wl_registry_add_listener(pd->registry, ®istry_listener, pd);
|
|
wl_display_roundtrip(wayland->display);
|
|
|
|
if (pd->manager == NULL) {
|
|
g_warning("Unable to initialize Window mode: Wayland compositor does not "
|
|
"support wlr-foreign-toplevel-management protocol");
|
|
return;
|
|
}
|
|
|
|
zwlr_foreign_toplevel_manager_v1_add_listener(
|
|
pd->manager, &foreign_toplevel_manager_listener, pd);
|
|
/* fetch initial set of windows */
|
|
wl_display_roundtrip(wayland->display);
|
|
pd->visible = TRUE;
|
|
}
|
|
|
|
static void toplevels_list_item_free(gpointer data,
|
|
G_GNUC_UNUSED gpointer user_data) {
|
|
foreign_toplevel_handle_free((ForeignToplevelHandle *)data);
|
|
}
|
|
|
|
static void wayland_window_private_free(WaylandWindowModePrivateData *pd) {
|
|
if (pd->toplevels) {
|
|
g_list_foreach(pd->toplevels, toplevels_list_item_free, NULL);
|
|
g_list_free(pd->toplevels);
|
|
pd->toplevels = NULL;
|
|
}
|
|
|
|
if (pd->registry) {
|
|
wl_registry_destroy(pd->registry);
|
|
pd->registry = NULL;
|
|
}
|
|
|
|
if (pd->manager) {
|
|
zwlr_foreign_toplevel_manager_v1_stop(pd->manager);
|
|
pd->manager = NULL;
|
|
wl_display_roundtrip(pd->wayland->display);
|
|
}
|
|
|
|
if (pd->window_regex) {
|
|
g_regex_unref(pd->window_regex);
|
|
}
|
|
|
|
g_free(pd);
|
|
}
|
|
|
|
static int wayland_window_mode_init(Mode *sw) {
|
|
/**
|
|
* Called on startup when enabled (in modi list)
|
|
*/
|
|
if (mode_get_private_data(sw) == NULL) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)g_malloc0(
|
|
sizeof(WaylandWindowModePrivateData));
|
|
mode_set_private_data(sw, (void *)pd);
|
|
|
|
get_wayland_window(sw);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static unsigned int wayland_window_mode_get_num_entries(const Mode *sw) {
|
|
const WaylandWindowModePrivateData *pd =
|
|
(const WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
|
|
g_return_val_if_fail(pd != NULL, 0);
|
|
|
|
return g_list_length(pd->toplevels);
|
|
}
|
|
|
|
static ModeMode wayland_window_mode_result(Mode *sw, int mretv,
|
|
G_GNUC_UNUSED char **input,
|
|
unsigned int selected_line) {
|
|
ModeMode retv = MODE_EXIT;
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
|
|
g_return_val_if_fail(pd != NULL, retv);
|
|
|
|
if (mretv & MENU_NEXT) {
|
|
retv = NEXT_DIALOG;
|
|
} else if (mretv & MENU_PREVIOUS) {
|
|
retv = PREVIOUS_DIALOG;
|
|
} else if (mretv & MENU_QUICK_SWITCH) {
|
|
retv = (ModeMode)(mretv & MENU_LOWER_MASK);
|
|
} else if ((mretv & MENU_OK)) {
|
|
rofi_view_hide();
|
|
ForeignToplevelHandle *toplevel =
|
|
(ForeignToplevelHandle *)g_list_nth_data(pd->toplevels, selected_line);
|
|
foreign_toplevel_handle_activate(toplevel, pd->wayland->last_seat->seat);
|
|
wl_display_flush(pd->wayland->display);
|
|
|
|
} else if ((mretv & MENU_ENTRY_DELETE) == MENU_ENTRY_DELETE) {
|
|
ForeignToplevelHandle *toplevel =
|
|
(ForeignToplevelHandle *)g_list_nth_data(pd->toplevels, selected_line);
|
|
foreign_toplevel_handle_close(toplevel);
|
|
wl_display_flush(pd->wayland->display);
|
|
}
|
|
|
|
return retv;
|
|
}
|
|
|
|
static void wayland_window_mode_destroy(Mode *sw) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
|
|
if (pd == NULL) {
|
|
return;
|
|
}
|
|
|
|
wayland_window_private_free(pd);
|
|
mode_set_private_data(sw, NULL);
|
|
}
|
|
|
|
static int wayland_window_token_match(const Mode *sw, rofi_int_matcher **tokens,
|
|
unsigned int index) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
ForeignToplevelHandle *toplevel =
|
|
(ForeignToplevelHandle *)g_list_nth_data(pd->toplevels, index);
|
|
|
|
g_return_val_if_fail(toplevel != NULL, 0);
|
|
|
|
int match = TRUE;
|
|
|
|
if (tokens) {
|
|
for (int j = 0; match && tokens[j] != NULL; j++) {
|
|
int test = 0;
|
|
/* See comment in window.c;
|
|
* for each token we want to match at least one field.
|
|
*/
|
|
rofi_int_matcher *ftokens[2] = {tokens[j], NULL};
|
|
|
|
if ((pd->match_fields & WW_MATCH_FIELD_TITLE) &&
|
|
toplevel->title != NULL && toplevel->title[0] != '\0') {
|
|
test = helper_token_match(ftokens, toplevel->title);
|
|
}
|
|
|
|
if (test == tokens[j]->invert &&
|
|
(pd->match_fields & WW_MATCH_FIELD_APP_ID) &&
|
|
toplevel->app_id != NULL && toplevel->app_id[0] != '\0') {
|
|
test = helper_token_match(ftokens, toplevel->app_id);
|
|
}
|
|
|
|
if (test == 0) {
|
|
match = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return match;
|
|
}
|
|
|
|
static void helper_eval_add_str(GString *str, const char *input, int len,
|
|
int max_len, int nc) {
|
|
// g_utf8 does not work with NULL string.
|
|
const char *input_nn = input ? input : "";
|
|
// Both len and max_len are in characters, not bytes.
|
|
int spaces = 0;
|
|
if (len > 0) {
|
|
if (nc > len) {
|
|
int bl = g_utf8_offset_to_pointer(input_nn, len) - input_nn;
|
|
char *tmp = g_markup_escape_text(input_nn, bl);
|
|
g_string_append(str, tmp);
|
|
g_free(tmp);
|
|
} else {
|
|
spaces = len - nc;
|
|
char *tmp = g_markup_escape_text(input_nn, -1);
|
|
g_string_append(str, tmp);
|
|
g_free(tmp);
|
|
}
|
|
} else {
|
|
char *tmp = g_markup_escape_text(input_nn, -1);
|
|
g_string_append(str, tmp);
|
|
g_free(tmp);
|
|
if (len == 0) {
|
|
spaces = MAX(0, max_len - nc);
|
|
}
|
|
}
|
|
while (spaces--) {
|
|
g_string_append_c(str, ' ');
|
|
}
|
|
}
|
|
|
|
struct arg {
|
|
const WaylandWindowModePrivateData *pd;
|
|
ForeignToplevelHandle *toplevel;
|
|
};
|
|
|
|
static gboolean helper_eval_cb(const GMatchInfo *info, GString *str,
|
|
gpointer data) {
|
|
struct arg *d = (struct arg *)data;
|
|
gchar *match;
|
|
// Get the match
|
|
match = g_match_info_fetch(info, 0);
|
|
if (match != NULL) {
|
|
int l = 0;
|
|
if (match[2] == ':') {
|
|
l = (int)g_ascii_strtoll(&match[3], NULL, 10);
|
|
}
|
|
/* Most of the arguments are not supported on wayland */
|
|
switch (match[1]) {
|
|
case 't': /* title */
|
|
helper_eval_add_str(str, d->toplevel->title, l, d->pd->title_len,
|
|
d->toplevel->title_len);
|
|
break;
|
|
case 'a': /* app_id */
|
|
case 'c': /* class */
|
|
helper_eval_add_str(str, d->toplevel->app_id, l, d->pd->app_id_len,
|
|
d->toplevel->app_id_len);
|
|
break;
|
|
}
|
|
|
|
g_free(match);
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static char *_generate_display_string(const WaylandWindowModePrivateData *pd,
|
|
ForeignToplevelHandle *toplevel) {
|
|
|
|
struct arg d = {pd, toplevel};
|
|
char *res = g_regex_replace_eval(pd->window_regex, config.window_format, -1,
|
|
0, 0, helper_eval_cb, &d, NULL);
|
|
return g_strchomp(res);
|
|
}
|
|
|
|
static char *_get_display_value(const Mode *sw, unsigned int selected_line,
|
|
int *state, G_GNUC_UNUSED GList **attr_list,
|
|
int get_entry) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
|
|
g_return_val_if_fail(pd != NULL, NULL);
|
|
|
|
ForeignToplevelHandle *toplevel =
|
|
(ForeignToplevelHandle *)g_list_nth_data(pd->toplevels, selected_line);
|
|
|
|
if (toplevel == NULL || toplevel->state & TOPLEVEL_STATE_CLOSED) {
|
|
return get_entry ? g_strdup("Window has vanished") : NULL;
|
|
}
|
|
|
|
/* This may not work because layer-surface holds focus */
|
|
if (toplevel->state & TOPLEVEL_STATE_ACTIVATED) {
|
|
*state |= ACTIVE;
|
|
}
|
|
*state |= MARKUP;
|
|
|
|
return get_entry ? _generate_display_string(pd, toplevel) : NULL;
|
|
}
|
|
|
|
static cairo_surface_t *_get_icon(const Mode *sw, unsigned int selected_line,
|
|
unsigned int height) {
|
|
WaylandWindowModePrivateData *pd =
|
|
(WaylandWindowModePrivateData *)mode_get_private_data(sw);
|
|
const guint scale = display_scale();
|
|
|
|
g_return_val_if_fail(pd != NULL, NULL);
|
|
|
|
ForeignToplevelHandle *toplevel =
|
|
(ForeignToplevelHandle *)g_list_nth_data(pd->toplevels, selected_line);
|
|
|
|
/* some apps don't have app_id (WM_CLASS). this is fine */
|
|
if (toplevel == NULL || toplevel->app_id == NULL ||
|
|
toplevel->app_id[0] == '\0') {
|
|
return NULL;
|
|
}
|
|
|
|
if (toplevel->cached_icon_uid > 0 && toplevel->cached_icon_size == height &&
|
|
toplevel->cached_icon_scale == scale) {
|
|
return rofi_icon_fetcher_get(toplevel->cached_icon_uid);
|
|
}
|
|
|
|
/**
|
|
* Lookup icon by lowercase app_id.
|
|
* There's no API to request multiple names, so we do the same as XCB window
|
|
* mode and search for a lowercase WM_CLASS/app_id.
|
|
*/
|
|
gchar *app_id_lower = g_utf8_strdown(toplevel->app_id, -1);
|
|
toplevel->cached_icon_size = height;
|
|
toplevel->cached_icon_scale = scale;
|
|
toplevel->cached_icon_uid = rofi_icon_fetcher_query(app_id_lower, height);
|
|
g_free(app_id_lower);
|
|
|
|
return rofi_icon_fetcher_get(toplevel->cached_icon_uid);
|
|
}
|
|
|
|
#include "mode-private.h"
|
|
|
|
Mode wayland_window_mode = {.name = "window",
|
|
.cfg_name_key = "display-window",
|
|
._init = wayland_window_mode_init,
|
|
._destroy = wayland_window_mode_destroy,
|
|
._get_num_entries =
|
|
wayland_window_mode_get_num_entries,
|
|
._result = wayland_window_mode_result,
|
|
._token_match = wayland_window_token_match,
|
|
._get_display_value = _get_display_value,
|
|
._get_icon = _get_icon,
|
|
._get_completion = NULL,
|
|
._preprocess_input = NULL,
|
|
._get_message = NULL,
|
|
.private_data = NULL,
|
|
.free = NULL,
|
|
.type = MODE_TYPE_SWITCHER};
|
|
|
|
#endif // WINDOW_MODE
|