Beispiel #1
0
void remove_desktop(monitor_t *m, desktop_t *d)
{
	put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove %s %s\n", m->name, d->name);

	bool was_focused = (mon != NULL && d == mon->desk);
	bool was_active = (d == m->desk);
	history_remove(d, NULL, false);
	unlink_desktop(m, d);
	empty_desktop(m, d);
	free(d);

	ewmh_update_current_desktop();
	ewmh_update_number_of_desktops();
	ewmh_update_desktop_names();

	if (mon != NULL && m->desk != NULL) {
		if (was_focused) {
			update_focused();
		} else if (was_active) {
			activate_node(m, m->desk, m->desk->focus);
		}
	}

	put_status(SBSC_MASK_REPORT);
}
Beispiel #2
0
void select_desktop(desktop_t *d)
{
    if (d == NULL || d == mon->desk)
        return;

    PRINTF("select desktop %s\n", d->name);

    if (visible) {
        node_t *n = first_extrema(d->root);

        while (n != NULL) {
            window_show(n->client->window);
            n = next_leaf(n);
        }

        n = first_extrema(mon->desk->root);

        while (n != NULL) {
            window_hide(n->client->window);
            n = next_leaf(n);
        }
    }

    mon->last_desk = mon->desk;
    mon->desk = d;

    update_current();
    ewmh_update_current_desktop();
    put_status();
}
Beispiel #3
0
void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
{
	if (ms == md)
		return;

	desktop_t *dd = ms->desk;
	unlink_desktop(ms, d);
	insert_desktop(md, d);

	if (d == dd) {
		if (ms->desk != NULL)
			show_desktop(ms->desk);
		if (md->desk != d)
			hide_desktop(d);
	}

	for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
		translate_client(ms, md, n->client);

	arrange(md, d);

	if (d != dd && md->desk == d)
		show_desktop(d);

	history_transfer_desktop(md, d);

	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_current_desktop();
	put_status();
}
Beispiel #4
0
Datei: ewmh.c Projekt: xiaq/hlwm
void ewmh_update_all() {
    /* init many properties */
    ewmh_update_client_list();
    ewmh_update_client_list_stacking();
    ewmh_update_desktops();
    ewmh_update_current_desktop();
    ewmh_update_desktop_names();
}
Beispiel #5
0
bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
{
	if (ms == NULL || md == NULL || d == NULL || ms == md) {
		return false;
	}

	bool was_active = (d == ms->desk);

	unlink_desktop(ms, d);

	if (md->desk != NULL) {
		hide_desktop(d);
	}

	insert_desktop(md, d);
	history_transfer_desktop(md, d);

	if (was_active) {
		if (mon == ms) {
			focus_node(ms, NULL, NULL);
		} else {
			activate_node(ms, NULL, NULL);
		}
	}

	if (ms->sticky_count > 0 && was_active) {
		sticky_still = false;
		transfer_sticky_nodes(ms, d, ms->desk, d->root);
		sticky_still = true;
	}

	adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
	arrange(md, d);

	if (md->desk == d) {
		if (mon == md) {
			focus_node(md, d, d->focus);
		} else {
			activate_node(md, d, d->focus);
		}
	}

	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_current_desktop();

	put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
	put_status(SBSC_MASK_REPORT);

	return true;
}
Beispiel #6
0
void focus_desktop(monitor_t *m, desktop_t *d)
{
	focus_monitor(m);

	show_desktop(d);
	if (m->desk != d) {
		hide_desktop(m->desk);
	}

	m->desk = d;
	ewmh_update_current_desktop();

	put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus %s %s\n", m->name, d->name);
}
Beispiel #7
0
bool swap_monitors(monitor_t *m1, monitor_t *m2)
{
	if (m1 == NULL || m2 == NULL || m1 == m2) {
		return false;
	}

	put_status(SBSC_MASK_MONITOR_SWAP, "monitor_swap 0x%08X 0x%08X\n", m1->id, m2->id);

	if (mon_head == m1) {
		mon_head = m2;
	} else if (mon_head == m2) {
		mon_head = m1;
	}
	if (mon_tail == m1) {
		mon_tail = m2;
	} else if (mon_tail == m2) {
		mon_tail = m1;
	}

	monitor_t *p1 = m1->prev;
	monitor_t *n1 = m1->next;
	monitor_t *p2 = m2->prev;
	monitor_t *n2 = m2->next;

	if (p1 != NULL && p1 != m2) {
		p1->next = m2;
	}
	if (n1 != NULL && n1 != m2) {
		n1->prev = m2;
	}
	if (p2 != NULL && p2 != m1) {
		p2->next = m1;
	}
	if (n2 != NULL && n2 != m1) {
		n2->prev = m1;
	}

	m1->prev = p2 == m1 ? m2 : p2;
	m1->next = n2 == m1 ? m2 : n2;
	m2->prev = p1 == m2 ? m1 : p1;
	m2->next = n1 == m2 ? m1 : n1;

	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_desktop_viewport();
	ewmh_update_current_desktop();

	put_status(SBSC_MASK_REPORT);
	return true;
}
Beispiel #8
0
void focus_monitor(monitor_t *m)
{
    if (mon == m)
        return;

    PRINTF("focus monitor %s\n", m->name);

    mon = m;

    if (pointer_follows_monitor)
        center_pointer(m);

    ewmh_update_current_desktop();
    put_status();
}
Beispiel #9
0
void select_monitor(monitor_t *m)
{
    if (m == NULL || mon == m)
        return;

    PRINTF("select monitor %s\n", m->name);

    focus_node(m, m->desk, m->desk->focus, true);

    last_mon = mon;
    mon = m;

    ewmh_update_current_desktop();
    put_status();
}
Beispiel #10
0
void focus_desktop(monitor_t *m, desktop_t *d)
{
	focus_monitor(m);

	if (d == mon->desk)
		return;

	PRINTF("focus desktop %s\n", d->name);

	show_desktop(d);
	hide_desktop(mon->desk);

	mon->desk = d;

	ewmh_update_current_desktop();
	put_status();
}
Beispiel #11
0
void swap_monitors(monitor_t *m1, monitor_t *m2)
{
	if (m1 == NULL || m2 == NULL || m1 == m2) {
		return;
	}

	if (mon_head == m1) {
		mon_head = m2;
	} else if (mon_head == m2) {
		mon_head = m1;
	}
	if (mon_tail == m1) {
		mon_tail = m2;
	} else if (mon_tail == m2) {
		mon_tail = m1;
	}

	monitor_t *p1 = m1->prev;
	monitor_t *n1 = m1->next;
	monitor_t *p2 = m2->prev;
	monitor_t *n2 = m2->next;

	if (p1 != NULL && p1 != m2) {
		p1->next = m2;
	}
	if (n1 != NULL && n1 != m2) {
		n1->prev = m2;
	}
	if (p2 != NULL && p2 != m1) {
		p2->next = m1;
	}
	if (n2 != NULL && n2 != m1) {
		n2->prev = m1;
	}

	m1->prev = p2 == m1 ? m2 : p2;
	m1->next = n2 == m1 ? m2 : n2;
	m2->prev = p1 == m2 ? m1 : p1;
	m2->next = n1 == m2 ? m1 : n1;

	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_current_desktop();

	put_status(SBSC_MASK_REPORT);
}
Beispiel #12
0
void swap_monitors(monitor_t *m1, monitor_t *m2)
{
    if (m1 == NULL || m2 == NULL || m1 == m2)
        return;

    if (mon_head == m1)
        mon_head = m2;
    else if (mon_head == m2)
        mon_head = m1;
    if (mon_tail == m1)
        mon_tail = m2;
    else if (mon_tail == m2)
        mon_tail = m1;

    monitor_t *p1 = m1->prev;
    monitor_t *n1 = m1->next;
    monitor_t *p2 = m2->prev;
    monitor_t *n2 = m2->next;

    if (p1 != NULL && p1 != m2)
        p1->next = m2;
    if (n1 != NULL && n1 != m2)
        n1->prev = m2;
    if (p2 != NULL && p2 != m1)
        p2->next = m1;
    if (n2 != NULL && n2 != m1)
        n2->prev = m1;

    m1->prev = p2 == m1 ? m2 : p2;
    m1->next = n2 == m1 ? m2 : n2;
    m2->prev = p1 == m2 ? m1 : p1;
    m2->next = n1 == m2 ? m1 : n1;

    ewmh_update_wm_desktops();
    ewmh_update_desktop_names();
    ewmh_update_current_desktop();
    put_status();
}
Beispiel #13
0
void focus_desktop(monitor_t *m, desktop_t *d)
{
	bool changed = (m != mon || m->desk != d);

	focus_monitor(m);

	if (m->desk != d) {
		if (focus_follows_pointer) {
			listen_enter_notify(d->root, false);
		}
		show_desktop(d);
		hide_desktop(m->desk);
		if (focus_follows_pointer) {
			listen_enter_notify(d->root, true);
		}
		m->desk = d;
	}

	if (changed) {
		ewmh_update_current_desktop();
		put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
	}
}
Beispiel #14
0
void remove_desktop(monitor_t *m, desktop_t *d)
{
	put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);

	remove_node(m, d, d->root);
	unlink_desktop(m, d);
	history_remove(d, NULL, false);
	free(d);

	ewmh_update_current_desktop();
	ewmh_update_number_of_desktops();
	ewmh_update_desktop_names();

	if (mon != NULL && m->desk == NULL) {
		if (m == mon) {
			focus_node(m, NULL, NULL);
		} else {
			activate_node(m, NULL, NULL);
		}
	}

	put_status(SBSC_MASK_REPORT);
}
Beispiel #15
0
bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
{
	if (d1 == NULL || d2 == NULL || d1 == d2 ||
	    (m1->desk == d1 && m1->sticky_count > 0) ||
	    (m2->desk == d2 && m2->sticky_count > 0)) {
		return false;
	}

	put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);

	bool d1_focused = (m1->desk == d1);
	bool d2_focused = (m2->desk == d2);

	if (m1 != m2) {
		if (m1->desk == d1) {
			m1->desk = d2;
		}
		if (m1->desk_head == d1) {
			m1->desk_head = d2;
		}
		if (m1->desk_tail == d1) {
			m1->desk_tail = d2;
		}
		if (m2->desk == d2) {
			m2->desk = d1;
		}
		if (m2->desk_head == d2) {
			m2->desk_head = d1;
		}
		if (m2->desk_tail == d2) {
			m2->desk_tail = d1;
		}
	} else {
		if (m1->desk == d1) {
			m1->desk = d2;
		} else if (m1->desk == d2) {
			m1->desk = d1;
		}
		if (m1->desk_head == d1) {
			m1->desk_head = d2;
		} else if (m1->desk_head == d2) {
			m1->desk_head = d1;
		}
		if (m1->desk_tail == d1) {
			m1->desk_tail = d2;
		} else if (m1->desk_tail == d2) {
			m1->desk_tail = d1;
		}
	}

	desktop_t *p1 = d1->prev;
	desktop_t *n1 = d1->next;
	desktop_t *p2 = d2->prev;
	desktop_t *n2 = d2->next;

	if (p1 != NULL && p1 != d2) {
		p1->next = d2;
	}
	if (n1 != NULL && n1 != d2) {
		n1->prev = d2;
	}
	if (p2 != NULL && p2 != d1) {
		p2->next = d1;
	}
	if (n2 != NULL && n2 != d1) {
		n2->prev = d1;
	}

	d1->prev = p2 == d1 ? d2 : p2;
	d1->next = n2 == d1 ? d2 : n2;
	d2->prev = p1 == d2 ? d1 : p1;
	d2->next = n1 == d2 ? d1 : n1;

	if (m1 != m2) {
		adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
		adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
		history_swap_desktops(m1, d1, m2, d2);
		arrange(m1, d2);
		arrange(m2, d1);
	}

	if (d1_focused && !d2_focused) {
		hide_desktop(d1);
		show_desktop(d2);
	} else if (!d1_focused && d2_focused) {
		show_desktop(d1);
		hide_desktop(d2);
	}

	if (d1 == mon->desk) {
		focus_node(m2, d1, d1->focus);
	} else if (d1 == m2->desk) {
		activate_node(m2, d1, d1->focus);
	}

	if (d2 == mon->desk) {
		focus_node(m1, d2, d2->focus);
	} else if (d2 == m1->desk) {
		activate_node(m1, d2, d2->focus);
	}

	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_current_desktop();

	put_status(SBSC_MASK_REPORT);

	return true;
}
Beispiel #16
0
bool update_monitors(void)
{
	xcb_randr_get_screen_resources_reply_t *sres = xcb_randr_get_screen_resources_reply(dpy, xcb_randr_get_screen_resources(dpy, root), NULL);
	if (sres == NULL) {
		return false;
	}

	monitor_t *m, *mm = NULL;

	int len = xcb_randr_get_screen_resources_outputs_length(sres);
	xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_outputs(sres);

	xcb_randr_get_output_info_cookie_t cookies[len];
	for (int i = 0; i < len; i++) {
		cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);
	}

	for (m = mon_head; m != NULL; m = m->next) {
		m->wired = false;
	}

	for (int i = 0; i < len; i++) {
		xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
		if (info != NULL) {
			if (info->crtc != XCB_NONE) {
				xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
				if (cir != NULL) {
					xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
					mm = get_monitor_by_randr_id(outputs[i]);
					if (mm != NULL) {
						update_root(mm, &rect);
						mm->wired = true;
					} else {
						mm = make_monitor(&rect, XCB_NONE);
						char *name = (char *)xcb_randr_get_output_info_name(info);
						int len = xcb_randr_get_output_info_name_length(info);
						size_t name_size = MIN(sizeof(mm->name), (size_t) len + 1);
						snprintf(mm->name, name_size, "%s", name);
						mm->randr_id = outputs[i];
						add_monitor(mm);
					}
				}
				free(cir);
			} else if (!remove_disabled_monitors && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
				m = get_monitor_by_randr_id(outputs[i]);
				if (m != NULL) {
					m->wired = true;
				}
			}
		}
		free(info);
	}

	xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
	if (gpo != NULL) {
		pri_mon = get_monitor_by_randr_id(gpo->output);
	}
	free(gpo);

	/* handle overlapping monitors */
	if (merge_overlapping_monitors) {
		m = mon_head;
		while (m != NULL) {
			monitor_t *next = m->next;
			if (m->wired) {
				monitor_t *mb = mon_head;
				while (mb != NULL) {
					monitor_t *mb_next = mb->next;
					if (m != mb && mb->wired && contains(m->rectangle, mb->rectangle)) {
						if (mm == mb) {
							mm = m;
						}
						if (next == mb) {
							next = mb_next;
						}
						merge_monitors(mb, m);
						remove_monitor(mb);
					}
					mb = mb_next;
				}
			}
			m = next;
		}
	}

	/* merge and remove disconnected monitors */
	if (remove_unplugged_monitors) {
		m = mon_head;
		while (m != NULL) {
			monitor_t *next = m->next;
			if (!m->wired) {
				merge_monitors(m, mm);
				remove_monitor(m);
			}
			m = next;
		}
	}

	/* add one desktop to each new monitor */
	for (m = mon_head; m != NULL; m = m->next) {
		if (m->desk == NULL) {
			add_desktop(m, make_desktop(NULL, XCB_NONE));
		}
	}

	if (!running && mon != NULL) {
		if (pri_mon != NULL) {
			mon = pri_mon;
		}
		center_pointer(mon->rectangle);
		ewmh_update_current_desktop();
	}

	free(sres);

	return (mon != NULL);
}
Beispiel #17
0
void swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
{
	if (d1 == NULL || d2 == NULL || d1 == d2)
		return;

	PRINTF("swap desktops %s %s\n", d1->name, d2->name);

	bool d1_focused = (m1->desk == d1);
	bool d2_focused = (m2->desk == d2);

	if (m1 != m2) {
		if (m1->desk == d1)
			m1->desk = d2;
		if (m1->desk_head == d1)
			m1->desk_head = d2;
		if (m1->desk_tail == d1)
			m1->desk_tail = d2;
		if (m2->desk == d2)
			m2->desk = d1;
		if (m2->desk_head == d2)
			m2->desk_head = d1;
		if (m2->desk_tail == d2)
			m2->desk_tail = d1;
	} else {
		if (m1->desk_head == d1)
			m1->desk_head = d2;
		else if (m1->desk_head == d2)
			m1->desk_head = d1;
		if (m1->desk_tail == d1)
			m1->desk_tail = d2;
		else if (m1->desk_tail == d2)
			m1->desk_tail = d1;
	}

	desktop_t *p1 = d1->prev;
	desktop_t *n1 = d1->next;
	desktop_t *p2 = d2->prev;
	desktop_t *n2 = d2->next;

	if (p1 != NULL && p1 != d2)
		p1->next = d2;
	if (n1 != NULL && n1 != d2)
		n1->prev = d2;
	if (p2 != NULL && p2 != d1)
		p2->next = d1;
	if (n2 != NULL && n2 != d1)
		n2->prev = d1;

	d1->prev = p2 == d1 ? d2 : p2;
	d1->next = n2 == d1 ? d2 : n2;
	d2->prev = p1 == d2 ? d1 : p1;
	d2->next = n1 == d2 ? d1 : n1;

	if (m1 != m2) {
		for (node_t *n = first_extrema(d1->root); n != NULL; n = next_leaf(n, d1->root))
			translate_client(m1, m2, n->client);
		for (node_t *n = first_extrema(d2->root); n != NULL; n = next_leaf(n, d2->root))
			translate_client(m2, m1, n->client);
		history_swap_desktops(m1, d1, m2, d2);
		arrange(m1, d2);
		arrange(m2, d1);
		if (d1_focused && !d2_focused) {
			hide_desktop(d1);
			show_desktop(d2);
		} else if (!d1_focused && d2_focused) {
			show_desktop(d1);
			hide_desktop(d2);
		}
	}

	update_input_focus();
	ewmh_update_wm_desktops();
	ewmh_update_desktop_names();
	ewmh_update_current_desktop();
	put_status();
}
Beispiel #18
0
bool import_monitors(void)
{
    PUTS("import monitors");
    xcb_randr_get_screen_resources_current_reply_t *sres = xcb_randr_get_screen_resources_current_reply(dpy, xcb_randr_get_screen_resources_current(dpy, root), NULL);
    if (sres == NULL)
        return false;

    monitor_t *m, *mm = NULL;

    int len = xcb_randr_get_screen_resources_current_outputs_length(sres);
    xcb_randr_output_t *outputs = xcb_randr_get_screen_resources_current_outputs(sres);

    xcb_randr_get_output_info_cookie_t cookies[len];
    for (int i = 0; i < len; i++)
        cookies[i] = xcb_randr_get_output_info(dpy, outputs[i], XCB_CURRENT_TIME);

    for (m = mon_head; m != NULL; m = m->next)
        m->wired = false;

    for (int i = 0; i < len; i++) {
        xcb_randr_get_output_info_reply_t *info = xcb_randr_get_output_info_reply(dpy, cookies[i], NULL);
        if (info != NULL) {
            if (info->crtc != XCB_NONE) {
                xcb_randr_get_crtc_info_reply_t *cir = xcb_randr_get_crtc_info_reply(dpy, xcb_randr_get_crtc_info(dpy, info->crtc, XCB_CURRENT_TIME), NULL);
                if (cir != NULL) {
                    xcb_rectangle_t rect = (xcb_rectangle_t) {cir->x, cir->y, cir->width, cir->height};
                    mm = get_monitor_by_id(outputs[i]);
                    if (mm != NULL) {
                        mm->rectangle = rect;
                        update_root(mm);
                        for (desktop_t *d = mm->desk_head; d != NULL; d = d->next)
                            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
                                translate_client(mm, mm, n->client);
                        arrange(mm, mm->desk);
                        mm->wired = true;
                        PRINTF("update monitor %s (0x%X)\n", mm->name, mm->id);
                    } else {
                        mm = add_monitor(rect);
                        char *name = (char *)xcb_randr_get_output_info_name(info);
                        size_t name_len = MIN(sizeof(mm->name), (size_t)xcb_randr_get_output_info_name_length(info) + 1);
                        snprintf(mm->name, name_len, "%s", name);
                        mm->id = outputs[i];
                        PRINTF("add monitor %s (0x%X)\n", mm->name, mm->id);
                    }
                }
                free(cir);
            } else if (!remove_disabled_monitor && info->connection != XCB_RANDR_CONNECTION_DISCONNECTED) {
                m = get_monitor_by_id(outputs[i]);
                if (m != NULL)
                    m->wired = true;
            }
        }
        free(info);
    }

    /* initially focus the primary monitor and add the first desktop to it */
    xcb_randr_get_output_primary_reply_t *gpo = xcb_randr_get_output_primary_reply(dpy, xcb_randr_get_output_primary(dpy, root), NULL);
    if (gpo != NULL) {
        pri_mon = get_monitor_by_id(gpo->output);
        if (!running && pri_mon != NULL) {
            if (mon != pri_mon)
                mon = pri_mon;
            add_desktop(pri_mon, make_desktop(NULL));
            ewmh_update_current_desktop();
        }
    }
    free(gpo);

    /* handle overlapping monitors */
    m = mon_head;
    while (m != NULL) {
        monitor_t *next = m->next;
        if (m->wired) {
            for (monitor_t *mb = mon_head; mb != NULL; mb = mb->next)
                if (mb != m && mb->wired && (m->desk == NULL || mb->desk == NULL)
                        && contains(mb->rectangle, m->rectangle)) {
                    if (mm == m)
                        mm = mb;
                    merge_monitors(m, mb);
                    remove_monitor(m);
                    break;
                }
        }
        m = next;
    }

    /* merge and remove disconnected monitors */
    m = mon_head;
    while (m != NULL) {
        monitor_t *next = m->next;
        if (!m->wired) {
            merge_monitors(m, mm);
            remove_monitor(m);
        }
        m = next;
    }

    /* add one desktop to each new monitor */
    for (m = mon_head; m != NULL; m = m->next)
        if (m->desk == NULL && (running || pri_mon == NULL || m != pri_mon))
            add_desktop(m, make_desktop(NULL));

    free(sres);
    update_motion_recorder();
    return (num_monitors > 0);
}
Beispiel #19
0
void restore_tree(char *file_path)
{
    if (file_path == NULL)
        return;

    FILE *snapshot = fopen(file_path, "r");
    if (snapshot == NULL) {
        warn("Restore tree: can't open file\n");
        return;
    }

    PUTS("restore tree");

    char line[MAXLEN];
    char name[MAXLEN];
    coordinates_t loc;
    monitor_t *m = NULL;
    desktop_t *d = NULL;
    node_t *n = NULL;
    num_clients = 0;
    unsigned int level, last_level = 0;

    while (fgets(line, sizeof(line), snapshot) != NULL) {
        unsigned int len = strlen(line);
        level = 0;

        while (level < len && isspace(line[level]))
            level++;

        if (level == 0) {
            int x, y, left, right, top, bottom;
            unsigned int w, h;
            char end = 0;
            name[0] = '\0';
            sscanf(line + level, "%s %ux%u%i%i %i,%i,%i,%i %c", name, &w, &h, &x, &y, &top, &right, &bottom, &left, &end);
            m = find_monitor(name);
            if (m == NULL)
                continue;
            m->rectangle = (xcb_rectangle_t) {x, y, w, h};
            m->top_padding = top;
            m->right_padding = right;
            m->bottom_padding = bottom;
            m->left_padding = left;
            if (end == '#')
                mon = m;
            else if (end == '~')
                last_mon = m;

        } else if (level == 2) {
            if (m == NULL)
                continue;
            int wg;
            char layout = 0, end = 0;
            name[0] = '\0';
            loc.desktop = NULL;
            sscanf(line + level, "%s %i %c %c", name, &wg, &layout, &end);
            locate_desktop(name, &loc);
            d = loc.desktop;
            if (d == NULL)
                continue;
            d->window_gap = wg;
            if (layout == 'M')
                d->layout = LAYOUT_MONOCLE;
            else if (layout == 'T')
                d->layout = LAYOUT_TILED;
            if (end == '@')
                m->desk = d;
            else if (end == '~')
                m->last_desk = d;

        } else {
            if (m == NULL || d == NULL)
                continue;
            node_t *birth = make_node();
            if (level == 4) {
                empty_desktop(d);
                d->root = birth;
            } else if (n != NULL) {
                if (level > last_level) {
                    n->first_child = birth;
                } else {
                    do {
                        n = n->parent;
                    } while (n != NULL && n->second_child != NULL);
                    if (n == NULL)
                        continue;
                    n->second_child = birth;
                }
                birth->parent = n;
            }
            n = birth;

            char br;
            if (isupper(line[level])) {
                char st;
                sscanf(line + level, "%c %c %lf", &st, &br, &n->split_ratio);
                if (st == 'H')
                    n->split_type = TYPE_HORIZONTAL;
                else if (st == 'V')
                    n->split_type = TYPE_VERTICAL;
            } else {
                client_t *c = make_client(XCB_NONE);
                num_clients++;
                char floating, transient, fullscreen, urgent, locked, sd, sm, end = 0;
                sscanf(line + level, "%c %s %X %u %hux%hu%hi%hi %c %c%c%c%c%c%c %c", &br, c->class_name, &c->window, &c->border_width, &c->floating_rectangle.width, &c->floating_rectangle.height, &c->floating_rectangle.x, &c->floating_rectangle.y, &sd, &floating, &transient, &fullscreen, &urgent, &locked, &sm, &end);
                c->floating = (floating == '-' ? false : true);
                c->transient = (transient == '-' ? false : true);
                c->fullscreen = (fullscreen == '-' ? false : true);
                c->urgent = (urgent == '-' ? false : true);
                c->locked = (locked == '-' ? false : true);
                n->split_mode = (sm == '-' ? MODE_AUTOMATIC : MODE_MANUAL);
                if (sd == 'U')
                    n->split_dir = DIR_UP;
                else if (sd == 'R')
                    n->split_dir = DIR_RIGHT;
                else if (sd == 'D')
                    n->split_dir = DIR_DOWN;
                else if (sd == 'L')
                    n->split_dir = DIR_LEFT;
                n->client = c;
                if (end == '*')
                    d->focus = n;
            }
            if (br == 'a')
                n->birth_rotation = 90;
            else if (br == 'c')
                n->birth_rotation = 270;
            else if (br == 'm')
                n->birth_rotation = 0;
        }
        last_level = level;
    }

    fclose(snapshot);

    for (monitor_t *m = mon_head; m != NULL; m = m->next)
        for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
            for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
                uint32_t values[] = {(focus_follows_pointer ? CLIENT_EVENT_MASK_FFP : CLIENT_EVENT_MASK)};
                xcb_change_window_attributes(dpy, n->client->window, XCB_CW_EVENT_MASK, values);
                if (n->client->floating) {
                    n->vacant = true;
                    update_vacant_state(n->parent);
                }
            }
    ewmh_update_current_desktop();
}