Skip to content

Commit

Permalink
Ignore title change events for unfocused window
Browse files Browse the repository at this point in the history
Resolves #9.

Signed-off-by: Artem Senichev <artemsen@gmail.com>
  • Loading branch information
artemsen committed Oct 19, 2023
1 parent 5b09819 commit fd20319
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 50 deletions.
32 changes: 16 additions & 16 deletions src/layouts.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,40 @@

#include <stdlib.h>

/** State descriptor: window and its layout. */
/** State descriptor: window key and its layout. */
struct state {
unsigned long window;
unsigned long key;
int layout;
};
static struct state* states;
static int states_sz;

/**
* Find state description for specified window.
* Find state description for specified window key.
* @param[in] window window Id
* @return pointer to the state descriptor or NULL if not found
*/
static struct state* find_state(unsigned long window)
static struct state* find_state(unsigned long key)
{
for (int i = 0; i < states_sz; ++i) {
struct state* entry = &states[i];
if (entry->window == window) {
if (entry->key == key) {
return entry;
}
}
return NULL;
}

int get_layout(unsigned long window)
int get_layout(unsigned long key)
{
const struct state* entry = find_state(window);
const struct state* entry = find_state(key);
return entry ? entry->layout : INVALID_LAYOUT;
}

void put_layout(unsigned long window, int layout)
void put_layout(unsigned long key, int layout)
{
// search for existing descriptor
struct state* entry = find_state(window);
struct state* entry = find_state(key);
if (entry) {
entry->layout = layout;
return;
Expand All @@ -47,8 +47,8 @@ void put_layout(unsigned long window, int layout)
// search for free descriptor
for (int i = 0; i < states_sz; ++i) {
struct state* entry = &states[i];
if (entry->window == INVALID_WINDOW) {
entry->window = window;
if (entry->key == INVALID_KEY) {
entry->key = key;
entry->layout = layout;
return;
}
Expand All @@ -61,20 +61,20 @@ void put_layout(unsigned long window, int layout)
for (int i = old_sz; i < states_sz; ++i) {
struct state* entry = &states[i];
if (i == old_sz) {
entry->window = window;
entry->key = key;
entry->layout = layout;
} else {
entry->window = INVALID_WINDOW;
entry->key = INVALID_KEY;
}
}
}

void rm_layout(unsigned long window)
void rm_layout(unsigned long key)
{
struct state* entry = find_state(window);
struct state* entry = find_state(key);
if (entry) {
// mark descriptor as free cell
entry->window = INVALID_WINDOW;
entry->key = INVALID_KEY;
entry->layout = INVALID_LAYOUT;
}
}
14 changes: 7 additions & 7 deletions src/layouts.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@

#pragma once

#define INVALID_WINDOW 0
#define INVALID_KEY 0
#define INVALID_LAYOUT -1

/**
* Get layout information for specified window.
* @param[in] window window Id
* @param[in] key window key
* @return layout index, INVALID_LAYOUT if not found
*/
int get_layout(unsigned long window);
int get_layout(unsigned long key);

/**
* Put layout information into storage.
* @param[in] window window Id
* @param[in] key window key
* @param[in] layout keyboard layout index
*/
void put_layout(unsigned long window, int layout);
void put_layout(unsigned long key, int layout);

/**
* Remove layout information from storage.
* @param[in] window window Id
* @param[in] key window key
*/
void rm_layout(unsigned long window);
void rm_layout(unsigned long key);
74 changes: 51 additions & 23 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,52 +20,62 @@
// Convert timespec to milliseconds
#define TIMESPEC_MS(ts) (ts.tv_sec * 1000 + ts.tv_nsec / 1000000)

// Debug trace
#if 0
#define TRACE(fmt, ...) printf("%s: " fmt "\n", __func__, ##__VA_ARGS__)
#else
#define TRACE(fmt, ...)
#endif

/** Static context. */
struct context {
unsigned long last_window; ///< Identifier of the last focused window
unsigned long last_key; ///< Key of the last focused window
int last_wnd; ///< Identifier of the last focused window
int default_layout; ///< Default layout for new windows
int current_layout; ///< Current layout index
int switch_timeout; ///< Ignored time between layout change and focus lost
struct timespec switch_timestamp; ///< Timestamp of the last layout change
char** tabapps;
size_t tabapps_num;
char** tabapps; ///< List of tab-enbled applications
size_t tabapps_num; ///< Size of tabbaps list
};
static struct context ctx = {
.last_window = INVALID_WINDOW,
.last_key = INVALID_KEY,
.last_wnd = -1,
.default_layout = DEFAULT_LAYOUT,
.current_layout = INVALID_LAYOUT,
.switch_timeout = DEFAULT_TIMEOUT,
};

/* Generate unique window id. */
static unsigned long window_id(int wnd_id, const char* app_id, const char* title)
/* Generate unique window key. */
static unsigned long window_key(int wnd_id, const char* app_id, const char* title)
{
unsigned long id = wnd_id;
unsigned long key = wnd_id;

// check if the current container belongs to the web browser, we will
// use window title (which is a tab name) to generate unique id
for (size_t i = 0; i < ctx.tabapps_num; ++i) {
if (strcmp(app_id, ctx.tabapps[i]) == 0) {
// djb2 hash
id = 5381;
key = 5381;
while (*title) {
id = ((id << 5) + id) + *title++;
key = ((key << 5) + key) + *title++;
}
key += wnd_id;
break;
}
}

return id;
return key;
}

/** Focus change handler. */
static int on_focus_change(int wnd_id, const char* app_id, const char* title)
{
int layout;
const unsigned long window = window_id(wnd_id, app_id, title);
const unsigned long key = window_key(wnd_id, app_id, title);

// save current layout for previously focused window
if (ctx.last_window != INVALID_WINDOW &&
if (ctx.last_key != INVALID_KEY &&
ctx.current_layout != INVALID_LAYOUT) {
if (ctx.switch_timeout == 0) {
layout = ctx.current_layout;
Expand All @@ -82,40 +92,57 @@ static int on_focus_change(int wnd_id, const char* app_id, const char* title)
}
}
if (layout != INVALID_LAYOUT) {
put_layout(ctx.last_window, layout);
TRACE("store layout=%d, window=%d:0x%lx",
layout, ctx.last_wnd, ctx.last_key);
put_layout(ctx.last_key, layout);
}
}

// define layout for currently focused window
layout = get_layout(window);
layout = get_layout(key);
TRACE("found layout=%d, window=%d:0x%lx", layout, wnd_id, key);
if (layout == INVALID_LAYOUT && ctx.default_layout != INVALID_LAYOUT) {
layout = ctx.default_layout; // set default
}
if (layout == ctx.current_layout) {
layout = INVALID_LAYOUT; // already set
}

ctx.last_window = window;
ctx.last_key = key;
ctx.last_wnd = wnd_id;

TRACE("set layout=%d, window=%d:0x%lx", layout, wnd_id, key);
return layout;
}

/** Title change handler. */
static int on_title_change(int wnd_id, const char* app_id, const char* title)
{
if (ctx.last_wnd == wnd_id) {
TRACE("window_id=%d", wnd_id);
return on_focus_change(wnd_id, app_id, title);
}
return INVALID_LAYOUT;
}

/** Window close handler. */
static void on_window_close(int wnd_id, const char* app_id, const char* title)
{
const unsigned long window = window_id(wnd_id, app_id, title);
const unsigned long key = window_key(wnd_id, app_id, title);

rm_layout(window);
TRACE("window=%d:0x%lx", wnd_id, key);
rm_layout(key);

if (window == ctx.last_window) {
// reset last window id to prevent saving layout for the closed window
ctx.last_window = INVALID_WINDOW;
if (key == ctx.last_key) {
// reset last window key to prevent saving layout for the closed window
ctx.last_key = INVALID_KEY;
}
}

/** Keyboard layout change handler. */
static void on_layout_change(int layout)
{
TRACE("layout=%d, window=%d:0x%lx", layout, ctx.last_wnd, ctx.last_key);
ctx.current_layout = layout;
clock_gettime(CLOCK_MONOTONIC, &ctx.switch_timestamp);
}
Expand All @@ -127,15 +154,15 @@ static void set_tabapps(const char* ids)

// get number of app ids
size = 1;
for(const char* ptr = ids; *ptr; ++ptr) {
for (const char* ptr = ids; *ptr; ++ptr) {
if (*ptr == ',') {
++size;
}
}

// split into array
ctx.tabapps = malloc(size * sizeof(char*));
for(const char* ptr = ids; ; ++ptr) {
for (const char* ptr = ids;; ++ptr) {
if (!*ptr || *ptr == ',') {
const size_t len = ptr - ids;
char* id = malloc(len + 1 /* last null */);
Expand Down Expand Up @@ -217,5 +244,6 @@ int main(int argc, char* argv[])
set_tabapps(DEFAULT_TABAPPS);
}

return sway_monitor(on_focus_change, on_window_close, on_layout_change);
return sway_monitor(on_focus_change, on_title_change,
on_window_close, on_layout_change);
}
12 changes: 9 additions & 3 deletions src/sway.c
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ static int layout_index(struct json_object* msg)
return -1;
}

int sway_monitor(on_focus fn_focus, on_close fn_close, on_layout fn_layout)
int sway_monitor(on_focus fn_focus, on_title fn_title,
on_close fn_close, on_layout fn_layout)
{
int rc;

Expand All @@ -285,13 +286,18 @@ int sway_monitor(on_focus fn_focus, on_close fn_close, on_layout fn_layout)
const char* app_id = "";
const char* title = "";
const char* event_name = json_object_get_string(event_node);
if (strcmp(event_name, "focus") == 0 ||
strcmp(event_name, "title") == 0) {
if (strcmp(event_name, "focus") == 0) {
container_info(msg, &wnd_id, &app_id, &title);
const int layout = fn_focus(wnd_id, app_id, title);
if (layout >= 0) {
ipc_change_layout(sock, layout);
}
} else if (strcmp(event_name, "title") == 0) {
container_info(msg, &wnd_id, &app_id, &title);
const int layout = fn_title(wnd_id, app_id, title);
if (layout >= 0) {
ipc_change_layout(sock, layout);
}
} else if (strcmp(event_name, "close") == 0) {
container_info(msg, &wnd_id, &app_id, &title);
fn_close(wnd_id, app_id, title);
Expand Down
13 changes: 12 additions & 1 deletion src/sway.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
*/
typedef int (*on_focus)(int wnd_id, const char* app_id, const char* title);

/**
* Callback function: Window title change handler.
* @param[in] wnd_id identifier of currently focused window (container)
* @param[in] app_id application id
* @param[in] title title of the window
* @return keyboard layout to set, -1 to leave the current one
*/
typedef int (*on_title)(int wnd_id, const char* app_id, const char* title);

/**
* Callback function: Window close handler.
* @param[in] wnd_id identifier of currently focused window (container)
Expand All @@ -30,8 +39,10 @@ typedef void (*on_layout)(int layout);
* Connect to Sway IPC and start event monitoring.
* Function never returns unless errors occurred.
* @param[in] fn_focus event handler for focus change
* @param[in] fn_title event handler for title change
* @param[in] fn_close event handler for window close
* @param[in] fn_layout event handler for layout change
* @return error code
*/
int sway_monitor(on_focus fn_focus, on_close fn_close, on_layout fn_layout);
int sway_monitor(on_focus fn_focus, on_title fn_title,
on_close fn_close, on_layout fn_layout);

0 comments on commit fd20319

Please sign in to comment.