添加布局算法并在新建窗口时应用规则
This commit is contained in:
@@ -15,7 +15,7 @@ project(${APP_NAME}
|
|||||||
LANGUAGES C
|
LANGUAGES C
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(${APP_NAME} main.c utils.c)
|
add_executable(${APP_NAME} main.c utils.c layout.c)
|
||||||
|
|
||||||
# use C17
|
# use C17
|
||||||
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 17)
|
set_property(TARGET ${APP_NAME} PROPERTY C_STANDARD 17)
|
||||||
@@ -58,6 +58,7 @@ target_include_directories(${APP_NAME}
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${APP_NAME}
|
target_link_libraries(${APP_NAME}
|
||||||
|
PRIVATE m
|
||||||
PRIVATE ${GLIB_LIBRARIES}
|
PRIVATE ${GLIB_LIBRARIES}
|
||||||
PRIVATE ${CAIRO_XCB_LIBRARIES}
|
PRIVATE ${CAIRO_XCB_LIBRARIES}
|
||||||
PRIVATE ${LIB_XCB_NAME}
|
PRIVATE ${LIB_XCB_NAME}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include <xcb/xproto.h>
|
#include <xcb/xproto.h>
|
||||||
|
|
||||||
#include "action.h"
|
#include "action.h"
|
||||||
|
#include "layout.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
/* 壁纸列表,支持通配符和 shell 环境变量拼接 */
|
/* 壁纸列表,支持通配符和 shell 环境变量拼接 */
|
||||||
@@ -44,6 +45,12 @@ const char *const default_monitor_tags[] = {
|
|||||||
"1", "2", "3", "4", "5", "6", "7", "8", "9", NULL,
|
"1", "2", "3", "4", "5", "6", "7", "8", "9", NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const layout_t layouts[] = {
|
||||||
|
{"[]=", tile},
|
||||||
|
{"[M]", monocle},
|
||||||
|
{"><>", NULL},
|
||||||
|
};
|
||||||
|
|
||||||
/* 新建窗口规则,以 NULL 结束 */
|
/* 新建窗口规则,以 NULL 结束 */
|
||||||
const rule_t rules[] = {
|
const rule_t rules[] = {
|
||||||
/* client 固定规则 */
|
/* client 固定规则 */
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ struct monitor_t {
|
|||||||
int32_t monitor_x, monitor_y, window_x, window_y;
|
int32_t monitor_x, monitor_y, window_x, window_y;
|
||||||
uint32_t monitor_width, monitor_height, window_width, window_height;
|
uint32_t monitor_width, monitor_height, window_width, window_height;
|
||||||
|
|
||||||
uint32_t selected_tag;
|
uint32_t selected_tags;
|
||||||
char **tags;
|
char **tags;
|
||||||
|
|
||||||
/* 为了避免引入 cairo 相关头文件,这里用 void *
|
/* 为了避免引入 cairo 相关头文件,这里用 void *
|
||||||
@@ -65,8 +65,8 @@ typedef struct rule_t {
|
|||||||
/* 规则适用的屏幕总数,若设置为 0 ,则此条规则适用于任意屏幕数 */
|
/* 规则适用的屏幕总数,若设置为 0 ,则此条规则适用于任意屏幕数 */
|
||||||
const uint32_t monitor_amount;
|
const uint32_t monitor_amount;
|
||||||
|
|
||||||
uint32_t tags;
|
uint32_t tags; /* tags 必须设置为大于 0 才是有效设置 */
|
||||||
uint32_t monitor_index;
|
uint32_t monitor_index; /* 仅当 tags 为有效设置时该设置才生效 */
|
||||||
|
|
||||||
bool switch_to_tag;
|
bool switch_to_tag;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
|
|||||||
@@ -24,6 +24,13 @@ xcb_window_t get_root_window(void);
|
|||||||
bool get_window_visible(xcb_window_t window);
|
bool get_window_visible(xcb_window_t window);
|
||||||
xcb_window_t get_transient_window_for(xcb_window_t window);
|
xcb_window_t get_transient_window_for(xcb_window_t window);
|
||||||
|
|
||||||
|
typedef struct window_class_instance_t {
|
||||||
|
char class[128];
|
||||||
|
char instance[128];
|
||||||
|
} window_class_instance_t;
|
||||||
|
void get_window_class_instance(xcb_window_t window,
|
||||||
|
window_class_instance_t *class_instance);
|
||||||
|
|
||||||
typedef struct window_geometry_t {
|
typedef struct window_geometry_t {
|
||||||
int32_t x, y;
|
int32_t x, y;
|
||||||
uint32_t width, height, border_width;
|
uint32_t width, height, border_width;
|
||||||
|
|||||||
98
src/layout.c
Normal file
98
src/layout.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "layout.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
client_t *next_visible_client(client_t *c) {
|
||||||
|
while (c &&
|
||||||
|
(c->floating || c->fullscreen || c->maximize || !CLIENT_VISIBLE(c))) {
|
||||||
|
c = c->next;
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maximize_client(client_t *c) {
|
||||||
|
monitor_t *m = c->monitor;
|
||||||
|
c->x = m->window_x;
|
||||||
|
c->y = m->window_y;
|
||||||
|
c->width = m->window_width;
|
||||||
|
c->height = m->window_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fullscreen_client(client_t *c) {
|
||||||
|
monitor_t *m = c->monitor;
|
||||||
|
c->x = m->monitor_x;
|
||||||
|
c->y = m->monitor_y;
|
||||||
|
c->width = m->monitor_width;
|
||||||
|
c->height = m->monitor_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void monocle(monitor_t *monitor) {
|
||||||
|
client_t *c = next_visible_client(monitor->clients);
|
||||||
|
for (; c; c = next_visible_client(c->next)) maximize_client(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tile(monitor_t *monitor) {
|
||||||
|
uint32_t n;
|
||||||
|
client_t *c = NULL;
|
||||||
|
for (n = 0, c = next_visible_client(monitor->clients); c;
|
||||||
|
c = next_visible_client(c->next), ++n);
|
||||||
|
|
||||||
|
if (n == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t columns = (uint32_t)floor(sqrt(n));
|
||||||
|
if (columns * (columns + 1) <= n) {
|
||||||
|
columns++;
|
||||||
|
}
|
||||||
|
uint32_t rows_in_other_cols = n / columns;
|
||||||
|
uint32_t rows_in_main_col;
|
||||||
|
while ((rows_in_main_col = n - (columns - 1) * rows_in_other_cols) >
|
||||||
|
rows_in_other_cols) {
|
||||||
|
rows_in_other_cols++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t width_avg = monitor->window_width / columns;
|
||||||
|
uint32_t width_for_main_col =
|
||||||
|
monitor->window_width - (columns - 1) * width_avg;
|
||||||
|
uint32_t width_for_other_cols = width_avg;
|
||||||
|
|
||||||
|
uint32_t i = 0, row = 0, col = 0, row_count, width, height;
|
||||||
|
int32_t y = monitor->window_y, x = monitor->window_x;
|
||||||
|
for (c = next_visible_client(monitor->clients); c;
|
||||||
|
c = next_visible_client(c->next), i++) {
|
||||||
|
if (i < rows_in_main_col) {
|
||||||
|
row = i;
|
||||||
|
width = width_for_main_col;
|
||||||
|
row_count = rows_in_main_col;
|
||||||
|
} else {
|
||||||
|
width = width_for_other_cols;
|
||||||
|
row_count = rows_in_other_cols;
|
||||||
|
if (((i - rows_in_main_col) % row_count) == 0) {
|
||||||
|
col++;
|
||||||
|
row = 0;
|
||||||
|
x += col == 1 ? width_for_main_col : width_for_other_cols;
|
||||||
|
y = monitor->window_y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t h_avg = monitor->window_height / row_count;
|
||||||
|
uint32_t h_main = monitor->window_height - h_avg * (row_count - 1);
|
||||||
|
height = row == 0 ? h_main : h_avg;
|
||||||
|
c->x = x;
|
||||||
|
c->y = y;
|
||||||
|
c->width = width - 2 * c->border_width;
|
||||||
|
c->height = height - 2 * c->border_width;
|
||||||
|
y += height;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void calc_clients_geometry_of_monitor(monitor_t *monitor) {
|
||||||
|
if (monitor && monitor->layout && monitor->layout->arrange) {
|
||||||
|
monitor->layout->arrange(monitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
src/layout.h
Normal file
14
src/layout.h
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
#define CLIENT_VISIBLE(C) ((C)->tags & (C)->monitor->selected_tags)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取下一个需要被布局算法自动管理的可见 client
|
||||||
|
*/
|
||||||
|
client_t *next_visible_client(client_t *client);
|
||||||
|
void maximize_client(client_t *client);
|
||||||
|
void fullscreen_client(client_t *client);
|
||||||
|
void monocle(monitor_t *monitor);
|
||||||
|
void tile(monitor_t *monitor);
|
||||||
102
src/main.c
102
src/main.c
@@ -14,6 +14,7 @@
|
|||||||
#include "action.h"
|
#include "action.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "event-adapter.h"
|
#include "event-adapter.h"
|
||||||
|
#include "layout.h"
|
||||||
#include "renderer.h"
|
#include "renderer.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
@@ -124,6 +125,21 @@ static void print_window_list(window_list_t *window_list) {
|
|||||||
printf("window[%u]: 0x%x\n", i, window_list->list[i]);
|
printf("window[%u]: 0x%x\n", i, window_list->list[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
static char *bool2string(bool b) { return b ? "true" : "false"; }
|
||||||
|
static void print_client(client_t *c) {
|
||||||
|
if (!c) return;
|
||||||
|
|
||||||
|
printf("======= client info ========\n");
|
||||||
|
printf("window: 0x%x, x: %d, y: %d, w: %u, h: %u, bw: %u\n", c->window, c->x,
|
||||||
|
c->y, c->width, c->height, c->border_width);
|
||||||
|
printf("floating: %s, fullscreen: %s, maximize: %s\n",
|
||||||
|
bool2string(c->floating), bool2string(c->fullscreen),
|
||||||
|
bool2string(c->maximize));
|
||||||
|
printf("tags: %b, monitor selected tags: %b\n", c->tags,
|
||||||
|
c->monitor->selected_tags);
|
||||||
|
printf("name: %s\n", c->name);
|
||||||
|
printf("===== client info end ======\n");
|
||||||
|
}
|
||||||
/* 调试函数结束 */
|
/* 调试函数结束 */
|
||||||
|
|
||||||
void init_monitors(void) {
|
void init_monitors(void) {
|
||||||
@@ -137,6 +153,7 @@ void init_monitors(void) {
|
|||||||
m->monitor_width = mr->width;
|
m->monitor_width = mr->width;
|
||||||
m->monitor_height = mr->height;
|
m->monitor_height = mr->height;
|
||||||
m->index = i;
|
m->index = i;
|
||||||
|
m->layout = &layouts[0];
|
||||||
|
|
||||||
if (prev_monitor) {
|
if (prev_monitor) {
|
||||||
prev_monitor->next = m;
|
prev_monitor->next = m;
|
||||||
@@ -153,7 +170,7 @@ void init_monitors(void) {
|
|||||||
}
|
}
|
||||||
for (monitor_t *m = monitors; m; m = m->next) {
|
for (monitor_t *m = monitors; m; m = m->next) {
|
||||||
m->tags = get_monitor_tags(i, m->index);
|
m->tags = get_monitor_tags(i, m->index);
|
||||||
m->selected_tag = 0x1;
|
m->selected_tags = 0x1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,15 +443,13 @@ void configure_request(configure_request_event_t *event) {
|
|||||||
if (!client) {
|
if (!client) {
|
||||||
configure_window(event->window, event->x, event->y, event->width,
|
configure_window(event->window, event->x, event->y, event->width,
|
||||||
event->height, event->border_width);
|
event->height, event->border_width);
|
||||||
flush_xcb_connection();
|
} else {
|
||||||
return;
|
configure_window(client->window, client->x, client->y, client->width,
|
||||||
|
client->height, client->border_width);
|
||||||
|
printf("configure request: %s\n", client->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("window: %u, parent: %u, sibling: %u\n", event->window, event->parent,
|
flush_xcb_connection();
|
||||||
event->sibling);
|
|
||||||
printf("x: %d, y: %d, w: %u, h: %u, bw: %u\n", event->x, event->y,
|
|
||||||
event->width, event->height, event->border_width);
|
|
||||||
printf("mask: %u, stack: %u\n", event->value_mask, event->stack_mode);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void map_request(map_request_event_t *event) {
|
void map_request(map_request_event_t *event) {
|
||||||
@@ -488,16 +503,81 @@ void update_client_name(client_t *client) {
|
|||||||
free(name);
|
free(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void apply_rules(client_t *client) { /* TODO: */ }
|
void apply_rules(client_t *client) {
|
||||||
|
uint32_t monitor_amount = 0;
|
||||||
|
for (monitor_t *m = monitors; m; m = m->next) monitor_amount++;
|
||||||
|
|
||||||
|
window_class_instance_t class_instance;
|
||||||
|
get_window_class_instance(client->window, &class_instance);
|
||||||
|
printf("class: %s, instance: %s\n", class_instance.class,
|
||||||
|
class_instance.instance);
|
||||||
|
|
||||||
|
bool switch_to_tag = 0;
|
||||||
|
for (int i = 0; i < LENGTH(rules); i++) {
|
||||||
|
const rule_t *r = &rules[i];
|
||||||
|
if (!((!r->monitor_amount || monitor_amount == r->monitor_amount) &&
|
||||||
|
(!r->title || strstr(client->name, r->title)) &&
|
||||||
|
(!r->class || strstr(class_instance.class, r->class)) &&
|
||||||
|
(!r->instance || strstr(class_instance.instance, r->instance)))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch_to_tag = r->switch_to_tag;
|
||||||
|
client->fullscreen = r->fullscreen;
|
||||||
|
client->maximize = r->maximize;
|
||||||
|
client->floating = r->floating;
|
||||||
|
|
||||||
|
if (r->tags) {
|
||||||
|
monitor_t *m = monitors;
|
||||||
|
for (uint32_t i = 0; i < r->monitor_index; i++) m = m ? m->next : NULL;
|
||||||
|
if (!m) continue;
|
||||||
|
uint32_t tags_mask = 0;
|
||||||
|
for (uint32_t i = 0; m->tags[i]; i++) {
|
||||||
|
tags_mask |= 1 << 0;
|
||||||
|
}
|
||||||
|
client->tags = r->tags & tags_mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!client->tags) {
|
||||||
|
client->monitor = current_monitor;
|
||||||
|
client->tags = current_monitor->selected_tags;
|
||||||
|
}
|
||||||
|
if (switch_to_tag) {
|
||||||
|
current_monitor = client->monitor;
|
||||||
|
current_monitor->selected_tags = client->tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("apply rules done\n");
|
||||||
|
}
|
||||||
|
|
||||||
void attach_client(client_t *client) {
|
void attach_client(client_t *client) {
|
||||||
client->next = client->monitor->clients;
|
client->next = client->monitor->clients;
|
||||||
client->monitor->clients = client;
|
client->monitor->clients = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
void show_hide_client(client_t *client) { /* TODO: */ }
|
void show_hide_client(client_t *client) {
|
||||||
|
if (!client || CLIENT_VISIBLE(client)) return;
|
||||||
|
client->x = client->monitor->monitor_x;
|
||||||
|
client->y = client->monitor->monitor_y + client->monitor->monitor_height;
|
||||||
|
configure_window(client->window, client->x, client->y, client->window,
|
||||||
|
client->height, client->border_width);
|
||||||
|
}
|
||||||
|
|
||||||
void apply_monitor_layout(monitor_t *monitor) { /* TODO: */ }
|
void apply_monitor_layout(monitor_t *monitor) {
|
||||||
|
if (!monitor || !monitor->layout || !monitor->layout->arrange) return;
|
||||||
|
|
||||||
|
monitor->layout->arrange(monitor);
|
||||||
|
print_client(monitor->clients);
|
||||||
|
for (client_t *c = next_visible_client(monitor->clients); c;
|
||||||
|
c = next_visible_client(c->next)) {
|
||||||
|
printf("apply window[0x%x] rect: x: %d, y: %d, w: %u, h: %u, bw: %u\n",
|
||||||
|
c->window, c->x, c->y, c->width, c->height, c->border_width);
|
||||||
|
configure_window(c->window, c->x, c->y, c->width, c->height,
|
||||||
|
c->border_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("apply monitor layout\n");
|
||||||
|
}
|
||||||
|
|
||||||
client_t *get_client_of_window(xcb_window_t window) {
|
client_t *get_client_of_window(xcb_window_t window) {
|
||||||
for (monitor_t *m = monitors; m; m = m->next) {
|
for (monitor_t *m = monitors; m; m = m->next) {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ void draw_bar(monitor_t *monitor, bool is_current_monitor,
|
|||||||
|
|
||||||
/* 绘制 tags */
|
/* 绘制 tags */
|
||||||
for (int i = 0; i < g_strv_length(monitor->tags); i++) {
|
for (int i = 0; i < g_strv_length(monitor->tags); i++) {
|
||||||
bool selected = (monitor->selected_tag >> i) & 0x1;
|
bool selected = (monitor->selected_tags >> i) & 0x1;
|
||||||
text = monitor->tags[i];
|
text = monitor->tags[i];
|
||||||
width = get_text_width(text) + tag_x_padding * 2;
|
width = get_text_width(text) + tag_x_padding * 2;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "window.h"
|
#include "window.h"
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -34,6 +35,28 @@ void set_window_class_instance(xcb_window_t window, const char *class,
|
|||||||
free(str);
|
free(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char broken[] = "broken";
|
||||||
|
|
||||||
|
void get_window_class_instance(xcb_window_t window,
|
||||||
|
window_class_instance_t *class_instance) {
|
||||||
|
xcb_get_property_cookie_t cookie = xcb_icccm_get_wm_class(conn, window);
|
||||||
|
xcb_icccm_get_wm_class_reply_t prop;
|
||||||
|
const char *class = NULL;
|
||||||
|
const char *instance = NULL;
|
||||||
|
if (xcb_icccm_get_wm_class_reply(conn, cookie, &prop, NULL)) {
|
||||||
|
class = prop.class_name;
|
||||||
|
instance = prop.instance_name;
|
||||||
|
} else {
|
||||||
|
class = broken;
|
||||||
|
instance = broken;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(class_instance->class, class, LENGTH(class_instance->class));
|
||||||
|
strncpy(class_instance->instance, instance, LENGTH(class_instance->instance));
|
||||||
|
|
||||||
|
xcb_icccm_get_wm_class_reply_wipe(&prop);
|
||||||
|
}
|
||||||
|
|
||||||
typedef struct visual_t {
|
typedef struct visual_t {
|
||||||
uint8_t depth;
|
uint8_t depth;
|
||||||
xcb_visualtype_t *visual;
|
xcb_visualtype_t *visual;
|
||||||
@@ -242,9 +265,8 @@ char *get_window_name(xcb_window_t window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
const char *broken = "broken";
|
char *name = ecalloc(LENGTH(broken), sizeof(char));
|
||||||
char *name = ecalloc(strlen(broken) + 1, sizeof(char));
|
strncpy(name, broken, LENGTH(broken));
|
||||||
strncpy(name, broken, strlen(broken));
|
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user