aboutsummaryrefslogtreecommitdiff
path: root/sway/debug-tree.c
blob: 973c6d88310755d94c195b2a318ab0a56a9aaed5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <pango/pangocairo.h>
#include <wlr/backend.h>
#include <wlr/render/wlr_texture.h>
#include <wlr/util/log.h>
#include "config.h"
#include "sway/debug.h"
#include "sway/input/input-manager.h"
#include "sway/input/seat.h"
#include "sway/output.h"
#include "sway/server.h"
#include "sway/tree/container.h"
#include "sway/tree/root.h"
#include "sway/tree/workspace.h"
#include "cairo.h"
#include "config.h"
#include "pango.h"

struct sway_debug debug;

static const char *layout_to_str(enum sway_container_layout layout) {
	switch (layout) {
	case L_HORIZ:
		return "L_HORIZ";
	case L_VERT:
		return "L_VERT";
	case L_STACKED:
		return "L_STACKED";
	case L_TABBED:
		return "L_TABBED";
	case L_NONE:
		return "L_NONE";
	}
	return "L_NONE";
}

static char *get_string(struct sway_node *node) {
	char *buffer = malloc(512);
	switch (node->type) {
	case N_ROOT:
		snprintf(buffer, 512, "N_ROOT id:%zd %.fx%.f@%.f,%.f", node->id,
				root->width, root->height, root->x, root->y);
		break;
	case N_OUTPUT:
		snprintf(buffer, 512, "N_OUTPUT id:%zd '%s' %dx%d@%d,%d", node->id,
				node->sway_output->wlr_output->name,
				node->sway_output->width,
				node->sway_output->height,
				node->sway_output->lx,
				node->sway_output->ly);
		break;
	case N_WORKSPACE:
		snprintf(buffer, 512, "N_WORKSPACE id:%zd '%s' %s %dx%d@%.f,%.f",
				node->id, node->sway_workspace->name,
				layout_to_str(node->sway_workspace->layout),
				node->sway_workspace->width, node->sway_workspace->height,
				node->sway_workspace->x, node->sway_workspace->y);
		break;
	case N_CONTAINER:
		snprintf(buffer, 512, "N_CONTAINER id:%zd '%s' %s %.fx%.f@%.f,%.f",
				node->id, node->sway_container->title,
				layout_to_str(node->sway_container->layout),
				node->sway_container->width, node->sway_container->height,
				node->sway_container->x, node->sway_container->y);
		break;
	}
	return buffer;
}

static list_t *get_children(struct sway_node *node) {
	switch (node->type) {
	case N_ROOT:
		return root->outputs;
	case N_OUTPUT:
		return node->sway_output->workspaces;
	case N_WORKSPACE:
		return node->sway_workspace->tiling;
	case N_CONTAINER:
		return node->sway_container->children;
	}
	return NULL;
}

static int draw_node(cairo_t *cairo, struct sway_node *node,
		struct sway_node *focus, int x, int y) {
	int text_width, text_height;
	char *buffer = get_string(node);
	get_text_size(cairo, "monospace", &text_width, &text_height,
		1, false, buffer);
	cairo_save(cairo);
	cairo_rectangle(cairo, x + 2, y, text_width - 2, text_height);
	cairo_set_source_u32(cairo, 0xFFFFFFE0);
	cairo_fill(cairo);
	int height = text_height;
	list_t *children = get_children(node);
	if (children) {
		for (int i = 0; i < children->length; ++i) {
			// This is really dirty - the list contains specific structs but
			// we're casting them as nodes. This works because node is the first
			// item in each specific struct. This is acceptable because this is
			// debug code.
			struct sway_node *child = children->items[i];
			if (node_get_parent(child) == node) {
				cairo_set_source_u32(cairo, 0x000000FF);
			} else {
				cairo_set_source_u32(cairo, 0xFF0000FF);
			}
			height += draw_node(cairo, child, focus, x + 10, y + height);
		}
	}
	cairo_set_source_u32(cairo, 0xFFFFFFE0);
	cairo_rectangle(cairo, x, y, 2, height);
	cairo_fill(cairo);
	cairo_restore(cairo);
	cairo_move_to(cairo, x, y);
	if (focus == node) {
		cairo_set_source_u32(cairo, 0x0000FFFF);
	}
	pango_printf(cairo, "monospace", 1, false, buffer);
	free(buffer);
	return height;
}

void update_debug_tree() {
	if (!debug.render_tree) {
		return;
	}

	int width = 640, height = 480;
	for (int i = 0; i < root->outputs->length; ++i) {
		struct sway_output *output = root->outputs->items[i];
		if (output->width > width) {
			width = output->width;
		}
		if (output->height > height) {
			height = output->height;
		}
	}
	cairo_surface_t *surface =
		cairo_image_surface_create(CAIRO_FORMAT_ARGB32, width, height);
	cairo_t *cairo = cairo_create(surface);
	PangoContext *pango = pango_cairo_create_context(cairo);

	struct sway_seat *seat = input_manager_current_seat(input_manager);
	struct sway_node *focus = seat_get_focus(seat);

	cairo_set_source_u32(cairo, 0x000000FF);
	draw_node(cairo, &root->node, focus, 0, 0);

	cairo_surface_flush(surface);
	struct wlr_renderer *renderer = wlr_backend_get_renderer(server.backend);
	if (root->debug_tree) {
		wlr_texture_destroy(root->debug_tree);
	}
	unsigned char *data = cairo_image_surface_get_data(surface);
	int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
	struct wlr_texture *texture = wlr_texture_from_pixels(renderer,
		WL_SHM_FORMAT_ARGB8888, stride, width, height, data);
	root->debug_tree = texture;
	cairo_surface_destroy(surface);
	g_object_unref(pango);
	cairo_destroy(cairo);
}