2018-10-03 21:14:51 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2003-11-10 03:45:17 +00:00
|
|
|
/*
|
2024-01-21 17:35:18 +00:00
|
|
|
* picom - a compositor for X11
|
2003-11-10 03:45:17 +00:00
|
|
|
*
|
2024-01-21 17:35:18 +00:00
|
|
|
* Based on `compton` - Copyright (c) 2011-2013, Christopher Jeffrey
|
2012-02-27 04:00:12 +00:00
|
|
|
* Based on `xcompmgr` - Copyright (c) 2003, Keith Packard
|
2003-11-10 03:45:17 +00:00
|
|
|
*
|
2024-01-21 17:35:18 +00:00
|
|
|
* Copyright (c) 2019-2023, Yuxuan Shui
|
|
|
|
*
|
2018-10-03 21:24:12 +00:00
|
|
|
* See LICENSE-mit for more information.
|
2003-11-10 03:45:17 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-12-30 07:06:47 +00:00
|
|
|
#include <X11/Xlib-xcb.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <X11/Xlib.h>
|
2019-08-09 23:56:04 +00:00
|
|
|
#include <X11/Xutil.h>
|
2019-09-16 21:14:34 +00:00
|
|
|
#include <X11/extensions/sync.h>
|
2020-10-22 00:39:51 +00:00
|
|
|
#include <errno.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <inttypes.h>
|
2024-02-20 00:34:26 +00:00
|
|
|
#include <libgen.h>
|
2023-07-09 15:39:44 +00:00
|
|
|
#include <math.h>
|
2023-06-24 01:59:04 +00:00
|
|
|
#include <sched.h>
|
2023-06-10 13:08:49 +00:00
|
|
|
#include <stddef.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
2023-06-10 13:18:25 +00:00
|
|
|
#include <sys/resource.h>
|
2023-07-09 15:39:44 +00:00
|
|
|
#include <time.h>
|
2020-10-25 03:13:03 +00:00
|
|
|
#include <unistd.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <xcb/composite.h>
|
2018-09-22 22:44:44 +00:00
|
|
|
#include <xcb/damage.h>
|
2022-12-15 10:53:31 +00:00
|
|
|
#include <xcb/dpms.h>
|
2020-03-31 05:07:09 +00:00
|
|
|
#include <xcb/glx.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <xcb/present.h>
|
|
|
|
#include <xcb/randr.h>
|
2018-09-23 18:10:46 +00:00
|
|
|
#include <xcb/render.h>
|
2019-01-20 21:15:20 +00:00
|
|
|
#include <xcb/sync.h>
|
2024-02-14 17:30:39 +00:00
|
|
|
#include <xcb/xcb_aux.h>
|
2019-03-10 12:34:37 +00:00
|
|
|
#include <xcb/xfixes.h>
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2018-10-02 19:22:26 +00:00
|
|
|
#include <ev.h>
|
2019-03-24 04:47:25 +00:00
|
|
|
#include <test.h>
|
2018-10-02 19:22:26 +00:00
|
|
|
|
2019-01-18 23:30:44 +00:00
|
|
|
#include "common.h"
|
2018-12-15 18:47:21 +00:00
|
|
|
#include "compiler.h"
|
2019-06-09 18:25:34 +00:00
|
|
|
#include "config.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "err.h"
|
2024-02-20 00:34:26 +00:00
|
|
|
#include "inspect.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "kernel.h"
|
2019-10-23 18:27:30 +00:00
|
|
|
#include "picom.h"
|
2024-03-19 16:44:02 +00:00
|
|
|
#include "transition.h"
|
2024-02-20 00:41:55 +00:00
|
|
|
#include "win_defs.h"
|
2024-03-26 17:54:34 +00:00
|
|
|
#include "wm.h"
|
2018-09-29 22:30:19 +00:00
|
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
#include "opengl.h"
|
|
|
|
#endif
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "atom.h"
|
2019-03-10 12:34:37 +00:00
|
|
|
#include "backend/backend.h"
|
|
|
|
#include "c2.h"
|
2018-12-20 22:31:34 +00:00
|
|
|
#include "dbus.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "diagnostic.h"
|
2019-03-17 13:44:26 +00:00
|
|
|
#include "event.h"
|
2019-11-10 18:58:01 +00:00
|
|
|
#include "file_watch.h"
|
2019-04-14 00:01:44 +00:00
|
|
|
#include "list.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "log.h"
|
2019-03-17 15:17:48 +00:00
|
|
|
#include "options.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "region.h"
|
|
|
|
#include "render.h"
|
2023-06-10 13:08:49 +00:00
|
|
|
#include "statistics.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "types.h"
|
2019-04-03 07:36:02 +00:00
|
|
|
#include "uthash_extra.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "utils.h"
|
2023-07-09 15:39:44 +00:00
|
|
|
#include "vblank.h"
|
2024-03-23 13:48:40 +00:00
|
|
|
#include "win.h"
|
|
|
|
#include "x.h"
|
2018-11-10 21:52:42 +00:00
|
|
|
|
2018-11-27 00:32:08 +00:00
|
|
|
/// Get session_t pointer from a pointer to a member of session_t
|
2019-03-10 12:34:37 +00:00
|
|
|
#define session_ptr(ptr, member) \
|
|
|
|
({ \
|
|
|
|
const __typeof__(((session_t *)0)->member) *__mptr = (ptr); \
|
|
|
|
(session_t *)((char *)__mptr - offsetof(session_t, member)); \
|
|
|
|
})
|
2018-11-27 00:32:08 +00:00
|
|
|
|
2019-09-28 22:43:36 +00:00
|
|
|
static bool must_use redirect_start(session_t *ps);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2019-09-28 22:43:36 +00:00
|
|
|
static void unredirect(session_t *ps);
|
2018-09-06 18:17:26 +00:00
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
// === Global constants ===
|
2012-02-27 04:00:12 +00:00
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
/// Name strings for window types.
|
2024-02-20 00:41:55 +00:00
|
|
|
const struct wintype_info WINTYPES[] = {
|
|
|
|
[WINTYPE_UNKNOWN] = {"unknown", NULL},
|
|
|
|
#define X(name, type) [WINTYPE_##type] = {#name, "_NET_WM_WINDOW_TYPE_" #type}
|
|
|
|
X(desktop, DESKTOP),
|
|
|
|
X(dock, DOCK),
|
|
|
|
X(toolbar, TOOLBAR),
|
|
|
|
X(menu, MENU),
|
|
|
|
X(utility, UTILITY),
|
|
|
|
X(splash, SPLASH),
|
|
|
|
X(dialog, DIALOG),
|
|
|
|
X(normal, NORMAL),
|
|
|
|
X(dropdown_menu, DROPDOWN_MENU),
|
|
|
|
X(popup_menu, POPUP_MENU),
|
|
|
|
X(tooltip, TOOLTIP),
|
|
|
|
X(notification, NOTIFICATION),
|
|
|
|
X(combo, COMBO),
|
|
|
|
X(dnd, DND),
|
|
|
|
#undef X
|
2012-09-25 13:04:10 +00:00
|
|
|
};
|
|
|
|
|
2020-01-18 18:00:35 +00:00
|
|
|
// clang-format off
|
2013-03-16 14:54:43 +00:00
|
|
|
/// Names of backends.
|
2019-08-04 16:52:45 +00:00
|
|
|
const char *const BACKEND_STRS[] = {[BKEND_XRENDER] = "xrender",
|
|
|
|
[BKEND_GLX] = "glx",
|
|
|
|
[BKEND_XR_GLX_HYBRID] = "xr_glx_hybrid",
|
|
|
|
[BKEND_DUMMY] = "dummy",
|
2022-09-29 06:55:25 +00:00
|
|
|
[BKEND_EGL] = "egl",
|
2019-08-04 16:52:45 +00:00
|
|
|
NULL};
|
2020-01-18 18:00:35 +00:00
|
|
|
// clang-format on
|
2013-04-21 14:30:22 +00:00
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
// === Global variables ===
|
Feature: Issue #29: Alternative shadow blacklist implementation
- Add shadow blacklist feature, but a different implementation from
nicklan's. 5 matching modes (exact, starts-with, contains, wildcard,
PCRE) and 3 matching targets (window name, window class instance,
window general class). Not extensively tested, bugs to be expected.
It's slower for exact matching than nicklan's as it uses linear search
instead of hash table. Also, PCRE's JIT optimization may cause issues
on PaX kernels.
- Add dependency to libpcre. Could be made optional if we have a
graceful way to handle that in Makefile.
- Some matching functions are GNU extensions of glibc. So this version
may have troubles running on platforms not using glibc.
- Fix a bug that access freed memory blocks in set_fade_callcack() and
check_fade_fin(). valgrind found it out.
- Use WM_CLASS to detect client windows instead of WM_STATE. Some client
windows (like notification windows) have WM_CLASS but not WM_STATE.
- Mark the extents as damaged if shadow state changed in
determine_shadow().
- Rewrite wid_get_name(). Code clean-up.
- Two debugging options: DEBUG_WINDATA and DEBUG_WINMATCH.
- As the matching system is ready, it should be rather easy to add other
kinds of blacklists, like fading blacklist.
2012-09-22 03:42:39 +00:00
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
/// Pointer to current session, as a global variable. Only used by
|
2018-11-26 23:56:55 +00:00
|
|
|
/// xerror(), which could not have a pointer to current session passed in.
|
|
|
|
/// XXX Limit what xerror can access by not having this pointer
|
2012-11-19 01:46:07 +00:00
|
|
|
session_t *ps_g = NULL;
|
2003-11-24 17:11:00 +00:00
|
|
|
|
2019-03-17 15:17:48 +00:00
|
|
|
void set_root_flags(session_t *ps, uint64_t flags) {
|
2020-10-28 19:02:57 +00:00
|
|
|
log_debug("Setting root flags: %" PRIu64, flags);
|
2019-03-17 15:17:48 +00:00
|
|
|
ps->root_flags |= flags;
|
2020-03-31 03:55:50 +00:00
|
|
|
ps->pending_updates = true;
|
2019-03-17 15:17:48 +00:00
|
|
|
}
|
|
|
|
|
2019-10-23 18:27:30 +00:00
|
|
|
void quit(session_t *ps) {
|
2019-04-19 19:53:52 +00:00
|
|
|
ps->quit = true;
|
|
|
|
ev_break(ps->loop, EVBREAK_ALL);
|
|
|
|
}
|
|
|
|
|
2018-11-03 22:38:18 +00:00
|
|
|
/**
|
|
|
|
* Get current system clock in milliseconds.
|
|
|
|
*/
|
2019-03-30 09:07:21 +00:00
|
|
|
static inline int64_t get_time_ms(void) {
|
2019-03-10 12:34:37 +00:00
|
|
|
struct timespec tp;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &tp);
|
2019-03-30 09:07:21 +00:00
|
|
|
return (int64_t)tp.tv_sec * 1000 + (int64_t)tp.tv_nsec / 1000000;
|
2018-11-03 22:38:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-19 23:21:07 +00:00
|
|
|
enum vblank_callback_action check_render_finish(struct vblank_event *e attr_unused, void *ud) {
|
|
|
|
auto ps = (session_t *)ud;
|
|
|
|
if (!ps->backend_busy) {
|
|
|
|
return VBLANK_CALLBACK_DONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct timespec render_time;
|
|
|
|
bool completed =
|
|
|
|
ps->backend_data->ops->last_render_time(ps->backend_data, &render_time);
|
|
|
|
if (!completed) {
|
|
|
|
// Render hasn't completed yet, we can't start another render.
|
|
|
|
// Check again at the next vblank.
|
|
|
|
log_debug("Last render did not complete during vblank, msc: "
|
|
|
|
"%" PRIu64,
|
|
|
|
ps->last_msc);
|
|
|
|
return VBLANK_CALLBACK_AGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// The frame has been finished and presented, record its render time.
|
|
|
|
if (ps->o.debug_options.smart_frame_pacing) {
|
|
|
|
int render_time_us =
|
|
|
|
(int)(render_time.tv_sec * 1000000L + render_time.tv_nsec / 1000L);
|
|
|
|
render_statistics_add_render_time_sample(
|
|
|
|
&ps->render_stats, render_time_us + (int)ps->last_schedule_delay);
|
|
|
|
log_verbose("Last render call took: %d (gpu) + %d (cpu) us, "
|
|
|
|
"last_msc: %" PRIu64,
|
|
|
|
render_time_us, (int)ps->last_schedule_delay, ps->last_msc);
|
|
|
|
}
|
|
|
|
ps->backend_busy = false;
|
|
|
|
return VBLANK_CALLBACK_DONE;
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
enum vblank_callback_action
|
|
|
|
collect_vblank_interval_statistics(struct vblank_event *e, void *ud) {
|
|
|
|
auto ps = (session_t *)ud;
|
|
|
|
double vblank_interval = NAN;
|
|
|
|
assert(ps->frame_pacing);
|
|
|
|
assert(ps->vblank_scheduler);
|
|
|
|
|
2023-12-19 23:07:32 +00:00
|
|
|
if (!ps->o.debug_options.smart_frame_pacing) {
|
|
|
|
// We don't need to collect statistics if we are not doing smart frame
|
|
|
|
// pacing.
|
|
|
|
return VBLANK_CALLBACK_DONE;
|
|
|
|
}
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
// TODO(yshui): this naive method of estimating vblank interval does not handle
|
|
|
|
// the variable refresh rate case very well. This includes the case
|
|
|
|
// of a VRR enabled monitor; or a monitor that's turned off, in which
|
|
|
|
// case the vblank events might slow down or stop all together.
|
|
|
|
// I tried using DPMS to detect monitor power state, and stop adding
|
|
|
|
// samples when the monitor is off, but I had a hard time to get it
|
|
|
|
// working reliably, there are just too many corner cases.
|
|
|
|
|
|
|
|
// Don't add sample again if we already collected statistics for this vblank
|
|
|
|
if (ps->last_msc < e->msc) {
|
|
|
|
if (ps->last_msc_instant != 0) {
|
|
|
|
auto frame_count = e->msc - ps->last_msc;
|
|
|
|
auto frame_time =
|
|
|
|
(int)((e->ust - ps->last_msc_instant) / frame_count);
|
|
|
|
if (frame_count == 1) {
|
|
|
|
render_statistics_add_vblank_time_sample(
|
|
|
|
&ps->render_stats, frame_time);
|
2024-02-13 23:10:16 +00:00
|
|
|
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
|
|
|
|
"ust: "
|
|
|
|
"%" PRIu64,
|
2023-07-09 15:39:44 +00:00
|
|
|
frame_count, frame_time, e->ust);
|
|
|
|
} else {
|
2024-02-13 23:10:16 +00:00
|
|
|
log_trace("Frame count %" PRIu64 ", frame time: %d us, "
|
|
|
|
"msc: "
|
2023-07-09 15:39:44 +00:00
|
|
|
"%" PRIu64 ", not adding sample.",
|
|
|
|
frame_count, frame_time, e->ust);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ps->last_msc_instant = e->ust;
|
|
|
|
ps->last_msc = e->msc;
|
|
|
|
} else if (ps->last_msc > e->msc) {
|
|
|
|
log_warn("PresentCompleteNotify msc is going backwards, last_msc: "
|
|
|
|
"%" PRIu64 ", current msc: %" PRIu64,
|
|
|
|
ps->last_msc, e->msc);
|
2024-01-14 15:41:10 +00:00
|
|
|
ps->last_msc_instant = 0;
|
|
|
|
ps->last_msc = 0;
|
2023-07-09 15:39:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vblank_interval = render_statistics_get_vblank_time(&ps->render_stats);
|
|
|
|
log_trace("Vblank interval estimate: %f us", vblank_interval);
|
|
|
|
if (vblank_interval == 0) {
|
|
|
|
// We don't have enough data for vblank interval estimate, schedule
|
|
|
|
// another vblank event.
|
|
|
|
return VBLANK_CALLBACK_AGAIN;
|
|
|
|
}
|
|
|
|
return VBLANK_CALLBACK_DONE;
|
|
|
|
}
|
2023-12-19 23:14:15 +00:00
|
|
|
|
|
|
|
void schedule_render(session_t *ps, bool triggered_by_vblank);
|
|
|
|
|
|
|
|
/// vblank callback scheduled by schedule_render, when a render is ongoing.
|
2023-07-09 15:39:44 +00:00
|
|
|
///
|
2023-12-19 23:14:15 +00:00
|
|
|
/// Check if previously queued render has finished, and reschedule render if it has.
|
|
|
|
enum vblank_callback_action reschedule_render_at_vblank(struct vblank_event *e, void *ud) {
|
2023-07-09 15:39:44 +00:00
|
|
|
auto ps = (session_t *)ud;
|
|
|
|
assert(ps->frame_pacing);
|
|
|
|
assert(ps->render_queued);
|
|
|
|
assert(ps->vblank_scheduler);
|
|
|
|
|
2023-12-19 23:14:15 +00:00
|
|
|
log_verbose("Rescheduling render at vblank, msc: %" PRIu64, e->msc);
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
collect_vblank_interval_statistics(e, ud);
|
2023-12-19 23:21:07 +00:00
|
|
|
check_render_finish(e, ud);
|
2023-07-09 15:39:44 +00:00
|
|
|
|
2023-12-19 23:14:15 +00:00
|
|
|
if (ps->backend_busy) {
|
2023-12-19 23:21:07 +00:00
|
|
|
return VBLANK_CALLBACK_AGAIN;
|
2023-12-19 23:07:32 +00:00
|
|
|
}
|
2023-07-09 15:39:44 +00:00
|
|
|
|
2023-12-19 23:14:15 +00:00
|
|
|
schedule_render(ps, false);
|
2023-07-09 15:39:44 +00:00
|
|
|
return VBLANK_CALLBACK_DONE;
|
|
|
|
}
|
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
/// How many seconds into the future should we start rendering the next frame.
|
2022-12-15 17:24:14 +00:00
|
|
|
///
|
|
|
|
/// Renders are scheduled like this:
|
|
|
|
///
|
2023-07-09 15:39:44 +00:00
|
|
|
/// 1. queue_redraw() queues a new render by calling schedule_render, if there
|
|
|
|
/// is no render currently scheduled. i.e. render_queued == false.
|
|
|
|
/// 2. then, we need to figure out the best time to start rendering. we need to
|
2023-12-19 23:14:15 +00:00
|
|
|
/// at least know when the next vblank will start, as we can't start render
|
2024-01-31 03:09:28 +00:00
|
|
|
/// before the current rendered frame is displayed on screen. we have this
|
2023-07-09 15:39:44 +00:00
|
|
|
/// information from the vblank scheduler, it will notify us when that happens.
|
|
|
|
/// we might also want to delay the rendering even further to reduce latency,
|
|
|
|
/// this is discussed below, in FUTURE WORKS.
|
|
|
|
/// 3. we schedule a render for that target point in time.
|
|
|
|
/// 4. draw_callback() is called at the schedule time (i.e. when scheduled
|
|
|
|
/// vblank event is delivered). Backend APIs are called to issue render
|
|
|
|
/// commands. render_queued is set to false, and backend_busy is set to true.
|
2023-06-22 23:17:35 +00:00
|
|
|
///
|
2023-12-19 23:14:15 +00:00
|
|
|
/// There are some considerations in step 2:
|
|
|
|
///
|
|
|
|
/// First of all, a vblank event being delivered
|
2023-07-09 15:39:44 +00:00
|
|
|
/// doesn't necessarily mean the frame has been displayed on screen. If a frame
|
|
|
|
/// takes too long to render, it might miss the current vblank, and will be
|
|
|
|
/// displayed on screen during one of the subsequent vblanks. So in
|
|
|
|
/// schedule_render_at_vblank, we ask the backend to see if it has finished
|
|
|
|
/// rendering. if not, render_queued is unchanged, and another vblank is
|
|
|
|
/// scheduled; otherwise, draw_callback_impl will be scheduled to be call at
|
2023-12-19 23:14:15 +00:00
|
|
|
/// an appropriate time. Second, we might not have rendered for the previous vblank,
|
|
|
|
/// in which case the last vblank event we received could be many frames in the past,
|
|
|
|
/// so we can't make scheduling decisions based on that. So we always schedule
|
|
|
|
/// a vblank event when render is queued, and make scheduling decisions when the
|
|
|
|
/// event is delivered.
|
2023-06-10 13:08:49 +00:00
|
|
|
///
|
2023-07-09 15:39:44 +00:00
|
|
|
/// All of the above is what happens when frame_pacing is true. Otherwise
|
|
|
|
/// render_in_progress is either QUEUED or IDLE, and queue_redraw will always
|
|
|
|
/// schedule a render to be started immediately. PresentCompleteNotify will not
|
|
|
|
/// be received, and handle_end_of_vblank will not be called.
|
|
|
|
///
|
|
|
|
/// The `triggered_by_timer` parameter is used to indicate whether this function
|
|
|
|
/// is triggered by a steady timer, i.e. we are rendering for each vblank. The
|
|
|
|
/// other case is when we stop rendering for a while because there is no changes
|
|
|
|
/// on screen, then something changed and schedule_render is triggered by a
|
|
|
|
/// DamageNotify. The idea is that when the schedule is triggered by a steady
|
|
|
|
/// timer, schedule_render will be called at a predictable offset into each
|
|
|
|
/// vblank.
|
|
|
|
///
|
|
|
|
/// # FUTURE WORKS
|
|
|
|
///
|
|
|
|
/// As discussed in step 2 above, we might want to delay the rendering even
|
|
|
|
/// further. If we know the time it takes to render a frame, and the interval
|
|
|
|
/// between vblanks, we can try to schedule the render to start at a point in
|
|
|
|
/// time that's closer to the next vblank. We should be able to get this
|
|
|
|
/// information by doing statistics on the render time of previous frames, which
|
|
|
|
/// is available from the backends; and the interval between vblank events,
|
|
|
|
/// which is available from the vblank scheduler.
|
|
|
|
///
|
|
|
|
/// The code that does this is already implemented below, but disabled by
|
|
|
|
/// default. There are several problems with it, see bug #1072.
|
2023-06-22 23:17:35 +00:00
|
|
|
void schedule_render(session_t *ps, bool triggered_by_vblank attr_unused) {
|
2023-12-19 23:14:15 +00:00
|
|
|
// If the backend is busy, we will try again at the next vblank.
|
|
|
|
if (ps->backend_busy) {
|
|
|
|
// We should never have set backend_busy to true unless frame_pacing is
|
|
|
|
// enabled.
|
|
|
|
assert(ps->vblank_scheduler);
|
|
|
|
assert(ps->frame_pacing);
|
|
|
|
log_verbose("Backend busy, will reschedule render at next vblank.");
|
|
|
|
if (!vblank_scheduler_schedule(ps->vblank_scheduler,
|
|
|
|
reschedule_render_at_vblank, ps)) {
|
|
|
|
// TODO(yshui): handle error here
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-06-22 23:17:35 +00:00
|
|
|
// By default, we want to schedule render immediately, later in this function we
|
|
|
|
// might adjust that and move the render later, based on render timing statistics.
|
2022-12-15 17:24:14 +00:00
|
|
|
double delay_s = 0;
|
2023-06-22 23:17:35 +00:00
|
|
|
unsigned int divisor = 0;
|
|
|
|
struct timespec now;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
auto now_us = (uint64_t)now.tv_sec * 1000000 + (uint64_t)now.tv_nsec / 1000;
|
|
|
|
|
|
|
|
ps->next_render = now_us;
|
|
|
|
|
2022-12-15 17:24:14 +00:00
|
|
|
if (!ps->frame_pacing || !ps->redirected) {
|
2023-12-18 20:32:22 +00:00
|
|
|
// If not doing frame pacing, schedule a render immediately; if
|
|
|
|
// not redirected, we schedule immediately to have a chance to
|
|
|
|
// redirect. We won't have frame or render timing information
|
2023-06-22 23:17:35 +00:00
|
|
|
// anyway.
|
2023-12-18 20:32:22 +00:00
|
|
|
assert(!ev_is_active(&ps->draw_timer));
|
|
|
|
goto schedule;
|
2022-12-15 17:24:14 +00:00
|
|
|
}
|
|
|
|
|
2023-12-19 23:07:32 +00:00
|
|
|
// if ps->o.debug_options.smart_frame_pacing is false, we won't have any render
|
|
|
|
// time or vblank interval estimates, so we would naturally fallback to schedule
|
|
|
|
// render immediately.
|
2023-12-18 20:32:22 +00:00
|
|
|
auto render_budget = render_statistics_get_budget(&ps->render_stats);
|
2023-06-10 13:08:49 +00:00
|
|
|
auto frame_time = render_statistics_get_vblank_time(&ps->render_stats);
|
|
|
|
if (frame_time == 0) {
|
|
|
|
// We don't have enough data for render time estimates, maybe there's
|
2023-04-27 03:06:56 +00:00
|
|
|
// no frame rendered yet, or the backend doesn't support render timing
|
|
|
|
// information, schedule render immediately.
|
2023-12-18 20:32:22 +00:00
|
|
|
log_verbose("Not enough data for render time estimates.");
|
2022-12-15 17:24:14 +00:00
|
|
|
goto schedule;
|
|
|
|
}
|
|
|
|
|
2023-12-18 20:32:22 +00:00
|
|
|
if (render_budget >= frame_time) {
|
|
|
|
// If the estimated render time is already longer than the estimated
|
|
|
|
// vblank interval, there is no way we can make it. Instead of always
|
|
|
|
// dropping frames, we try desperately to catch up and schedule a
|
|
|
|
// render immediately.
|
|
|
|
log_verbose("Render budget: %u us >= frame time: %" PRIu32 " us",
|
|
|
|
render_budget, frame_time);
|
|
|
|
goto schedule;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto target_frame = (now_us + render_budget - ps->last_msc_instant) / frame_time + 1;
|
|
|
|
auto const deadline = ps->last_msc_instant + target_frame * frame_time;
|
2023-06-10 13:08:49 +00:00
|
|
|
unsigned int available = 0;
|
|
|
|
if (deadline > now_us) {
|
|
|
|
available = (unsigned int)(deadline - now_us);
|
|
|
|
}
|
2022-12-15 17:24:14 +00:00
|
|
|
|
2023-06-10 13:08:49 +00:00
|
|
|
if (available > render_budget) {
|
|
|
|
delay_s = (double)(available - render_budget) / 1000000.0;
|
|
|
|
ps->next_render = deadline - render_budget;
|
|
|
|
}
|
2023-06-22 23:17:35 +00:00
|
|
|
|
core: make sure unredirection happens when screen is off
So when the screen is off, we calls queue_redraw, hoping draw_callback
will be called and unredirects the screen. However, queue_redraw doesn't
queue another redraw when one is already queued. Redraws can be queued
in two ways: one is timer based, which is fine, because it will be
triggered no matter what; the other is frame based, which is triggered
by Present events.
When the screen is off, X server, depends on the driver, could send
abnormal Present events, which we ignore. But that also means queued
frame based redraw will stop being triggered.
Those two factors combined means sometimes unredirection does not happen
when the screen is off. Which means we aren't going to free the GL
context, which are still receiving Present events, but can't handle
them, because we are not rendering anything with GL. In the end all
these causes memory usage to balloon, until the screen is turned on and
we start rendering again.
And all these is not caught by leak checkers because this technically is
not a leak, as everything is eventually freed.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-03-07 17:03:26 +00:00
|
|
|
if (delay_s > 1) {
|
2023-06-10 13:08:49 +00:00
|
|
|
log_warn("Delay too long: %f s, render_budget: %d us, frame_time: "
|
|
|
|
"%" PRIu32 " us, now_us: %" PRIu64 " us, next_msc: %" PRIu64 " u"
|
|
|
|
"s",
|
|
|
|
delay_s, render_budget, frame_time, now_us, deadline);
|
core: make sure unredirection happens when screen is off
So when the screen is off, we calls queue_redraw, hoping draw_callback
will be called and unredirects the screen. However, queue_redraw doesn't
queue another redraw when one is already queued. Redraws can be queued
in two ways: one is timer based, which is fine, because it will be
triggered no matter what; the other is frame based, which is triggered
by Present events.
When the screen is off, X server, depends on the driver, could send
abnormal Present events, which we ignore. But that also means queued
frame based redraw will stop being triggered.
Those two factors combined means sometimes unredirection does not happen
when the screen is off. Which means we aren't going to free the GL
context, which are still receiving Present events, but can't handle
them, because we are not rendering anything with GL. In the end all
these causes memory usage to balloon, until the screen is turned on and
we start rendering again.
And all these is not caught by leak checkers because this technically is
not a leak, as everything is eventually freed.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2023-03-07 17:03:26 +00:00
|
|
|
}
|
2022-12-15 17:24:14 +00:00
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
log_verbose("Delay: %.6lf s, last_msc: %" PRIu64 ", render_budget: %d, "
|
2023-12-19 23:14:15 +00:00
|
|
|
"frame_time: %" PRIu32 ", now_us: %" PRIu64 ", next_render: %" PRIu64
|
|
|
|
", next_msc: %" PRIu64 ", divisor: "
|
|
|
|
"%d",
|
2023-07-09 15:39:44 +00:00
|
|
|
delay_s, ps->last_msc_instant, render_budget, frame_time, now_us,
|
2023-12-19 23:14:15 +00:00
|
|
|
ps->next_render, deadline, divisor);
|
2022-12-15 17:24:14 +00:00
|
|
|
|
|
|
|
schedule:
|
2023-07-09 15:39:44 +00:00
|
|
|
// If the backend is not busy, we just need to schedule the render at the
|
2023-12-19 23:14:15 +00:00
|
|
|
// specified time; otherwise we need to wait for the next vblank event and
|
|
|
|
// reschedule.
|
|
|
|
ps->last_schedule_delay = 0;
|
|
|
|
assert(!ev_is_active(&ps->draw_timer));
|
|
|
|
ev_timer_set(&ps->draw_timer, delay_s, 0);
|
|
|
|
ev_timer_start(ps->loop, &ps->draw_timer);
|
2023-06-22 23:17:35 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 19:22:26 +00:00
|
|
|
void queue_redraw(session_t *ps) {
|
2023-12-19 23:14:15 +00:00
|
|
|
log_verbose("Queue redraw, render_queued: %d, backend_busy: %d",
|
|
|
|
ps->render_queued, ps->backend_busy);
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
if (ps->render_queued) {
|
|
|
|
return;
|
2019-07-25 01:27:02 +00:00
|
|
|
}
|
2023-12-19 23:14:15 +00:00
|
|
|
ps->render_queued = true;
|
2023-12-18 20:32:22 +00:00
|
|
|
schedule_render(ps, false);
|
2018-10-02 19:22:26 +00:00
|
|
|
}
|
|
|
|
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 03:56:00 +00:00
|
|
|
/**
|
|
|
|
* Get a region of the screen size.
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static inline void get_screen_region(session_t *ps, region_t *res) {
|
|
|
|
pixman_box32_t b = {.x1 = 0, .y1 = 0, .x2 = ps->root_width, .y2 = ps->root_height};
|
|
|
|
pixman_region32_fini(res);
|
|
|
|
pixman_region32_init_rects(res, &b, 1);
|
Convert XfixesRegion to pixman region
Re-did the painting logic, and document it.
It is unclear to me what is the previous painting logic. But the current
one is basically this:
1. Go through all windows top to bottom, and put visible windows (not
unmapped, opacity > 0, etc) into a linked list, from bottom to top
2. Accumulate a region of ignore on each window, which is basically the
region of screen that is obscured by all the windows above current
one.
3. Paint all the visible windows from bottom to top. Subtract the region
of ignore from the painting region. If we need to paint shadow, we
subtract the body of the window from the shadow painting region too,
because we don't want shadow behind the window.
4. region of ignore is invalidated when window stack change, an
window on top moved or changed shape, when window changed between
opaque and transparent, etc.
Notes:
It is unclear whether all the different shapes of a window (extents,
noframe, border, bounding shape, etc) are calculated correctly or not.
It is unclear if window shape related events are handled correctly or
not. Need more testing.
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2018-09-30 03:56:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void add_damage(session_t *ps, const region_t *damage) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Ignore damage when screen isn't redirected
|
2020-12-04 01:07:14 +00:00
|
|
|
if (!ps->redirected) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-12-04 01:07:14 +00:00
|
|
|
}
|
2018-09-09 01:29:45 +00:00
|
|
|
|
2020-12-04 01:07:14 +00:00
|
|
|
if (!damage) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return;
|
2020-12-04 01:07:14 +00:00
|
|
|
}
|
|
|
|
log_trace("Adding damage: ");
|
|
|
|
dump_region(damage);
|
2024-03-24 03:22:58 +00:00
|
|
|
|
|
|
|
auto cursor = &ps->damage_ring.damages[ps->damage_ring.cursor];
|
|
|
|
pixman_region32_union(cursor, cursor, (region_t *)damage);
|
2018-09-09 01:29:45 +00:00
|
|
|
}
|
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
// === Fading ===
|
2012-09-26 07:37:11 +00:00
|
|
|
|
2012-09-19 12:49:16 +00:00
|
|
|
/**
|
|
|
|
* Get the time left before next fading point.
|
|
|
|
*
|
|
|
|
* In milliseconds.
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static double fade_timeout(session_t *ps) {
|
|
|
|
auto now = get_time_ms();
|
2023-06-29 04:39:36 +00:00
|
|
|
if (ps->o.fade_delta + ps->fade_time < now) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return 0;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2018-12-21 16:25:28 +00:00
|
|
|
|
2019-03-30 09:07:21 +00:00
|
|
|
auto diff = ps->o.fade_delta + ps->fade_time - now;
|
2011-11-04 08:41:56 +00:00
|
|
|
|
2019-03-30 09:07:21 +00:00
|
|
|
diff = clamp(diff, 0, ps->o.fade_delta * 2);
|
2004-07-08 07:07:26 +00:00
|
|
|
|
2019-03-30 09:07:21 +00:00
|
|
|
return (double)diff / 1000.0;
|
2004-07-08 07:07:26 +00:00
|
|
|
}
|
|
|
|
|
2012-09-19 12:49:16 +00:00
|
|
|
/**
|
|
|
|
* Run fading on a window.
|
|
|
|
*
|
|
|
|
* @param steps steps of fading
|
2019-02-12 01:53:50 +00:00
|
|
|
* @return whether we are still in fading mode
|
2012-09-19 12:49:16 +00:00
|
|
|
*/
|
2024-03-22 02:41:48 +00:00
|
|
|
static bool run_fade(struct managed_win **_w, unsigned int steps) {
|
2019-04-17 22:14:45 +00:00
|
|
|
auto w = *_w;
|
2024-03-19 16:44:02 +00:00
|
|
|
log_trace("Process fading for window %s (%#010x), steps: %u", w->name, w->base.id,
|
|
|
|
steps);
|
2024-03-21 18:01:02 +00:00
|
|
|
if (w->number_of_animations == 0) {
|
2019-03-27 15:30:37 +00:00
|
|
|
// We have reached target opacity.
|
|
|
|
// We don't call win_check_fade_finished here because that could destroy
|
|
|
|
// the window, but we still need the damage info from this window
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- was fading but finished");
|
2019-03-10 12:34:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2024-03-19 16:44:02 +00:00
|
|
|
log_trace("|- fading, opacity: %lf", animatable_get(&w->opacity));
|
|
|
|
animatable_step(&w->opacity, steps);
|
|
|
|
animatable_step(&w->blur_opacity, steps);
|
|
|
|
log_trace("|- opacity updated: %lf (%u steps)", animatable_get(&w->opacity), steps);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-19 16:44:02 +00:00
|
|
|
// Note even if the animatable is not animating anymore at this point, we still
|
|
|
|
// want to run preprocess one last time to finish state transition. So return true
|
|
|
|
// in that case too.
|
2019-03-10 12:34:37 +00:00
|
|
|
return true;
|
2004-07-08 07:07:26 +00:00
|
|
|
}
|
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
// === Windows ===
|
2012-02-08 22:45:08 +00:00
|
|
|
|
2019-05-24 23:06:41 +00:00
|
|
|
/**
|
|
|
|
* Update current active window based on EWMH _NET_ACTIVE_WIN.
|
|
|
|
*
|
|
|
|
* Does not change anything if we fail to get the attribute or the window
|
|
|
|
* returned could not be found.
|
|
|
|
*/
|
|
|
|
void update_ewmh_active_win(session_t *ps) {
|
|
|
|
// Search for the window
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_window_t wid = wid_get_prop_window(&ps->c, ps->c.screen_info->root,
|
|
|
|
ps->atoms->a_NET_ACTIVE_WINDOW);
|
2024-03-26 17:54:34 +00:00
|
|
|
auto w = wm_find_by_client(ps->wm, wid);
|
2019-05-24 23:06:41 +00:00
|
|
|
|
|
|
|
// Mark the window focused. No need to unfocus the previous one.
|
|
|
|
if (w) {
|
|
|
|
win_set_focused(ps, w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-13 13:38:55 +00:00
|
|
|
/**
|
|
|
|
* Recheck currently focused window and set its <code>w->focused</code>
|
2012-11-19 01:46:07 +00:00
|
|
|
* to true.
|
2012-09-13 13:38:55 +00:00
|
|
|
*
|
2012-11-19 01:46:07 +00:00
|
|
|
* @param ps current session
|
2012-09-13 13:38:55 +00:00
|
|
|
* @return struct _win of currently focused window, NULL if not found
|
|
|
|
*/
|
2019-05-24 23:06:41 +00:00
|
|
|
static void recheck_focus(session_t *ps) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Use EWMH _NET_ACTIVE_WINDOW if enabled
|
|
|
|
if (ps->o.use_ewmh_active_win) {
|
|
|
|
update_ewmh_active_win(ps);
|
2019-05-24 21:40:36 +00:00
|
|
|
return;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2012-11-04 10:11:08 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Determine the currently focused window so we can apply appropriate
|
|
|
|
// opacity on it
|
2024-03-26 13:41:25 +00:00
|
|
|
xcb_generic_error_t *e = NULL;
|
|
|
|
auto reply = xcb_get_input_focus_reply(ps->c.c, xcb_get_input_focus(ps->c.c), &e);
|
|
|
|
if (reply == NULL) {
|
|
|
|
// Not able to get input focus means very not good things...
|
|
|
|
log_error_x_error(e, "Failed to get focused window.");
|
|
|
|
free(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
xcb_window_t wid = reply->focus;
|
|
|
|
free(reply);
|
2012-09-13 13:38:55 +00:00
|
|
|
|
2024-03-26 13:41:25 +00:00
|
|
|
if (wid == XCB_NONE || wid == XCB_INPUT_FOCUS_POINTER_ROOT ||
|
|
|
|
wid == ps->c.screen_info->root) {
|
|
|
|
// Focus is not on a toplevel.
|
|
|
|
return;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2012-09-13 13:38:55 +00:00
|
|
|
|
2024-03-26 13:41:25 +00:00
|
|
|
// Trace upwards until we reach the toplevel containing the focus window.
|
|
|
|
while (true) {
|
|
|
|
auto tree = xcb_query_tree_reply(ps->c.c, xcb_query_tree(ps->c.c, wid), &e);
|
|
|
|
if (tree == NULL) {
|
|
|
|
// xcb_query_tree probably fails if you run picom when X is
|
|
|
|
// somehow initializing (like add it in .xinitrc). In this case
|
|
|
|
// just leave it alone.
|
|
|
|
log_error_x_error(e, "Failed to query window tree.");
|
|
|
|
free(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto parent = tree->parent;
|
|
|
|
free(tree);
|
2012-09-13 13:38:55 +00:00
|
|
|
|
2024-03-26 13:41:25 +00:00
|
|
|
if (parent == ps->c.screen_info->root) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
wid = parent;
|
|
|
|
}
|
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
auto w = wm_find_managed(ps->wm, wid);
|
2013-10-21 14:17:01 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// And we set the focus state here
|
|
|
|
if (w) {
|
2024-03-26 13:41:25 +00:00
|
|
|
log_debug("%#010" PRIx32 " (%#010" PRIx32 " \"%s\") focused.", wid,
|
|
|
|
w->base.id, w->name);
|
2019-05-24 22:13:24 +00:00
|
|
|
win_set_focused(ps, w);
|
2024-03-26 13:41:25 +00:00
|
|
|
} else {
|
|
|
|
log_warn("Focus window %#010" PRIx32 " not found.", wid);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2012-01-31 22:10:30 +00:00
|
|
|
}
|
|
|
|
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
/**
|
|
|
|
* Rebuild cached <code>screen_reg</code>.
|
|
|
|
*/
|
|
|
|
static void rebuild_screen_reg(session_t *ps) {
|
|
|
|
get_screen_region(ps, &ps->screen_reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Rebuild <code>shadow_exclude_reg</code>.
|
|
|
|
*/
|
|
|
|
static void rebuild_shadow_exclude_reg(session_t *ps) {
|
|
|
|
bool ret = parse_geometry(ps, ps->o.shadow_exclude_reg_str, &ps->shadow_exclude_reg);
|
2023-08-10 22:38:22 +00:00
|
|
|
if (!ret) {
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
exit(1);
|
2023-08-10 22:38:22 +00:00
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Free up all the images and deinit the backend
|
|
|
|
static void destroy_backend(session_t *ps) {
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
// Wrapping up fading in progress
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(w);
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
|
|
|
|
if (ps->backend_data) {
|
2020-11-28 19:31:43 +00:00
|
|
|
// Unmapped windows could still have shadow images, but not pixmap
|
|
|
|
// images
|
|
|
|
assert(!w->win_image || w->state != WSTATE_UNMAPPED);
|
2024-02-19 01:09:00 +00:00
|
|
|
// In some cases, the window might have PIXMAP_STALE flag set:
|
|
|
|
// 1. If the window is unmapped. Their stale flags won't be
|
|
|
|
// handled until they are mapped.
|
|
|
|
// 2. If we haven't had chance to handle the stale flags. This
|
|
|
|
// could happen if we received a root ConfigureNotify
|
|
|
|
// _immidiately_ after we redirected.
|
2024-02-10 16:48:37 +00:00
|
|
|
win_clear_flags(w, WIN_FLAGS_PIXMAP_STALE);
|
2020-11-28 19:31:43 +00:00
|
|
|
win_release_images(ps->backend_data, w);
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
free_paint(ps, &w->paint);
|
2024-03-22 02:41:48 +00:00
|
|
|
|
|
|
|
if (w->state == WSTATE_DESTROYED) {
|
|
|
|
destroy_win_finish(ps, &w->base);
|
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
|
2022-07-17 16:49:35 +00:00
|
|
|
HASH_ITER2(ps->shaders, shader) {
|
|
|
|
if (shader->backend_shader != NULL) {
|
2022-07-17 20:51:12 +00:00
|
|
|
ps->backend_data->ops->destroy_shader(ps->backend_data,
|
|
|
|
shader->backend_shader);
|
2022-07-17 16:49:35 +00:00
|
|
|
shader->backend_shader = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
if (ps->backend_data && ps->root_image) {
|
|
|
|
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
|
|
|
|
ps->root_image = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->backend_data) {
|
|
|
|
// deinit backend
|
|
|
|
if (ps->backend_blur_context) {
|
|
|
|
ps->backend_data->ops->destroy_blur_context(
|
|
|
|
ps->backend_data, ps->backend_blur_context);
|
|
|
|
ps->backend_blur_context = NULL;
|
|
|
|
}
|
2022-08-25 04:21:19 +00:00
|
|
|
if (ps->shadow_context) {
|
|
|
|
ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
|
|
|
|
ps->shadow_context);
|
|
|
|
ps->shadow_context = NULL;
|
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
ps->backend_data->ops->deinit(ps->backend_data);
|
|
|
|
ps->backend_data = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool initialize_blur(session_t *ps) {
|
|
|
|
struct kernel_blur_args kargs;
|
|
|
|
struct gaussian_blur_args gargs;
|
|
|
|
struct box_blur_args bargs;
|
2019-12-20 19:31:00 +00:00
|
|
|
struct dual_kawase_blur_args dkargs;
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
|
|
|
|
void *args = NULL;
|
|
|
|
switch (ps->o.blur_method) {
|
|
|
|
case BLUR_METHOD_BOX:
|
|
|
|
bargs.size = ps->o.blur_radius;
|
|
|
|
args = (void *)&bargs;
|
|
|
|
break;
|
|
|
|
case BLUR_METHOD_KERNEL:
|
|
|
|
kargs.kernel_count = ps->o.blur_kernel_count;
|
|
|
|
kargs.kernels = ps->o.blur_kerns;
|
|
|
|
args = (void *)&kargs;
|
|
|
|
break;
|
|
|
|
case BLUR_METHOD_GAUSSIAN:
|
|
|
|
gargs.size = ps->o.blur_radius;
|
|
|
|
gargs.deviation = ps->o.blur_deviation;
|
|
|
|
args = (void *)&gargs;
|
|
|
|
break;
|
2019-12-20 19:31:00 +00:00
|
|
|
case BLUR_METHOD_DUAL_KAWASE:
|
|
|
|
dkargs.size = ps->o.blur_radius;
|
|
|
|
dkargs.strength = ps->o.blur_strength;
|
|
|
|
args = (void *)&dkargs;
|
|
|
|
break;
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
default: return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ps->backend_blur_context = ps->backend_data->ops->create_blur_context(
|
|
|
|
ps->backend_data, ps->o.blur_method, args);
|
|
|
|
return ps->backend_blur_context != NULL;
|
|
|
|
}
|
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
static int mark_pixmap_stale(struct win *w, void *data) {
|
|
|
|
struct session *ps = data;
|
|
|
|
if (!w->managed) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
auto mw = win_as_managed(w);
|
|
|
|
assert(mw->state != WSTATE_DESTROYED);
|
|
|
|
// We need to reacquire image
|
|
|
|
log_debug("Marking window %#010x (%s) for update after redirection", w->id, mw->name);
|
|
|
|
win_set_flags(mw, WIN_FLAGS_PIXMAP_STALE);
|
|
|
|
ps->pending_updates = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
/// Init the backend and bind all the window pixmap to backend images
|
|
|
|
static bool initialize_backend(session_t *ps) {
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
assert(!ps->backend_data);
|
|
|
|
// Reinitialize win_data
|
|
|
|
assert(backend_list[ps->o.backend]);
|
2023-08-02 21:48:57 +00:00
|
|
|
ps->backend_data =
|
|
|
|
backend_list[ps->o.backend]->init(ps, session_get_target_window(ps));
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
if (!ps->backend_data) {
|
|
|
|
log_fatal("Failed to initialize backend, aborting...");
|
|
|
|
quit(ps);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
ps->backend_data->ops = backend_list[ps->o.backend];
|
2022-08-25 04:21:19 +00:00
|
|
|
ps->shadow_context = ps->backend_data->ops->create_shadow_context(
|
|
|
|
ps->backend_data, ps->o.shadow_radius);
|
|
|
|
if (!ps->shadow_context) {
|
|
|
|
log_fatal("Failed to initialize shadow context, aborting...");
|
|
|
|
goto err;
|
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
|
|
|
|
if (!initialize_blur(ps)) {
|
|
|
|
log_fatal("Failed to prepare for background blur, aborting...");
|
2022-07-17 20:51:12 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create shaders
|
|
|
|
HASH_ITER2(ps->shaders, shader) {
|
|
|
|
assert(shader->backend_shader == NULL);
|
|
|
|
shader->backend_shader = ps->backend_data->ops->create_shader(
|
|
|
|
ps->backend_data, shader->source);
|
|
|
|
if (shader->backend_shader == NULL) {
|
|
|
|
log_warn("Failed to create shader for shader file %s, "
|
|
|
|
"this shader will not be used",
|
|
|
|
shader->key);
|
2022-08-11 03:53:14 +00:00
|
|
|
} else {
|
|
|
|
if (ps->backend_data->ops->get_shader_attributes) {
|
|
|
|
shader->attributes =
|
|
|
|
ps->backend_data->ops->get_shader_attributes(
|
|
|
|
ps->backend_data, shader->backend_shader);
|
|
|
|
} else {
|
|
|
|
shader->attributes = 0;
|
|
|
|
}
|
2022-11-18 10:13:49 +00:00
|
|
|
log_debug("Shader %s has attributes %" PRIu64,
|
|
|
|
shader->key, shader->attributes);
|
2022-07-17 20:51:12 +00:00
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
// wm_stack shouldn't include window that's not iterated by wm_foreach at
|
|
|
|
// this moment. Since there cannot be any fading windows.
|
|
|
|
wm_foreach(ps->wm, mark_pixmap_stale, ps);
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The old backends binds pixmap lazily, nothing to do here
|
|
|
|
return true;
|
2022-07-17 20:51:12 +00:00
|
|
|
err:
|
2022-08-25 04:21:19 +00:00
|
|
|
if (ps->shadow_context) {
|
|
|
|
ps->backend_data->ops->destroy_shadow_context(ps->backend_data,
|
|
|
|
ps->shadow_context);
|
|
|
|
ps->shadow_context = NULL;
|
|
|
|
}
|
2022-07-17 20:51:12 +00:00
|
|
|
ps->backend_data->ops->deinit(ps->backend_data);
|
|
|
|
ps->backend_data = NULL;
|
|
|
|
quit(ps);
|
|
|
|
return false;
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Handle configure event of the root window
|
|
|
|
static void configure_root(session_t *ps) {
|
2024-02-14 18:54:04 +00:00
|
|
|
// TODO(yshui) re-initializing backend should be done outside of the
|
|
|
|
// critical section. Probably set a flag and do it in draw_callback_impl.
|
2023-06-29 04:39:36 +00:00
|
|
|
auto r = XCB_AWAIT(xcb_get_geometry, ps->c.c, ps->c.screen_info->root);
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
if (!r) {
|
|
|
|
log_fatal("Failed to fetch root geometry");
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
log_info("Root configuration changed, new geometry: %dx%d", r->width, r->height);
|
|
|
|
bool has_root_change = false;
|
|
|
|
if (ps->redirected) {
|
|
|
|
// On root window changes
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
assert(ps->backend_data);
|
|
|
|
has_root_change = ps->backend_data->ops->root_change != NULL;
|
|
|
|
} else {
|
|
|
|
// Old backend can handle root change
|
|
|
|
has_root_change = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!has_root_change) {
|
|
|
|
// deinit/reinit backend and free up resources if the backend
|
|
|
|
// cannot handle root change
|
|
|
|
destroy_backend(ps);
|
|
|
|
}
|
|
|
|
free_paint(ps, &ps->tgt_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
ps->root_width = r->width;
|
|
|
|
ps->root_height = r->height;
|
|
|
|
|
|
|
|
rebuild_screen_reg(ps);
|
|
|
|
rebuild_shadow_exclude_reg(ps);
|
|
|
|
|
|
|
|
// Invalidate reg_ignore from the top
|
2024-03-26 17:54:34 +00:00
|
|
|
auto top_w = wm_stack_next_managed(ps->wm, wm_stack_end(ps->wm));
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
if (top_w) {
|
|
|
|
rc_region_unref(&top_w->reg_ignore);
|
|
|
|
top_w->reg_ignore_valid = false;
|
|
|
|
}
|
|
|
|
|
2024-02-14 18:54:04 +00:00
|
|
|
// Whether a window is fullscreen depends on the new screen
|
|
|
|
// size. So we need to refresh the fullscreen state of all
|
|
|
|
// windows.
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
2024-02-14 18:54:04 +00:00
|
|
|
win_update_is_fullscreen(ps, w);
|
|
|
|
}
|
|
|
|
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
if (ps->redirected) {
|
2024-03-24 03:22:58 +00:00
|
|
|
for (int i = 0; i < ps->damage_ring.count; i++) {
|
|
|
|
pixman_region32_clear(&ps->damage_ring.damages[i]);
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
}
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.cursor = ps->damage_ring.count - 1;
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
#ifdef CONFIG_OPENGL
|
|
|
|
// GLX root change callback
|
2022-08-21 05:00:43 +00:00
|
|
|
if (BKEND_GLX == ps->o.backend && ps->o.legacy_backends) {
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
glx_on_root_change(ps);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (has_root_change) {
|
|
|
|
if (ps->backend_data != NULL) {
|
|
|
|
ps->backend_data->ops->root_change(ps->backend_data, ps);
|
|
|
|
}
|
|
|
|
// Old backend's root_change is not a specific function
|
|
|
|
} else {
|
|
|
|
if (!initialize_backend(ps)) {
|
|
|
|
log_fatal("Failed to re-initialize backend after root "
|
|
|
|
"change, aborting...");
|
|
|
|
ps->quit = true;
|
2020-08-30 14:25:58 +00:00
|
|
|
/* TODO(yshui) only event handlers should request
|
|
|
|
* ev_break, otherwise it's too hard to keep track of what
|
|
|
|
* can break the event loop */
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
ev_break(ps->loop, EVBREAK_ALL);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Re-acquire the root pixmap.
|
|
|
|
root_damaged(ps);
|
|
|
|
}
|
|
|
|
force_repaint(ps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-17 15:17:48 +00:00
|
|
|
static void handle_root_flags(session_t *ps) {
|
|
|
|
if ((ps->root_flags & ROOT_FLAGS_SCREEN_CHANGE) != 0) {
|
2023-06-29 04:39:36 +00:00
|
|
|
if (ps->o.crop_shadow_to_monitor && ps->randr_exists) {
|
|
|
|
x_update_monitors(&ps->c, &ps->monitors);
|
2019-03-17 15:17:48 +00:00
|
|
|
}
|
2019-03-30 09:07:21 +00:00
|
|
|
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_SCREEN_CHANGE;
|
2019-03-17 15:17:48 +00:00
|
|
|
}
|
core: delayed handling of root ConfigureNotify
Previously, root ConfigureNotify is handled immediately, by resetting
the backend, which in turn releases all the window images. This puts all
the windows into a state where they don't have images attached, which
they really should be in when the screen is redirected.
(To expand a little, a window without images should only exist if:
* It's an unmanaged window.
* Screen is unredirected.)
Normally, this kind of window could be fine, as the next render phase
will re-acquire images for them. However, if a window in this state is
destroyed with fading enabled, then the render phase won't try to
acquire images for it, causing it to go into the main rendering function
without images attached, and trigger an assertion.
This commit delays the handling of root ConfigureNotify until the render
phase. This way, the images will be immediately re-acquired after they
are released, thus prevent this problem from happening.
Also adds a testcase for this.
Fixes #357
Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
2020-03-31 04:01:14 +00:00
|
|
|
|
|
|
|
if ((ps->root_flags & ROOT_FLAGS_CONFIGURED) != 0) {
|
|
|
|
configure_root(ps);
|
|
|
|
ps->root_flags &= ~(uint64_t)ROOT_FLAGS_CONFIGURED;
|
|
|
|
}
|
2019-03-17 15:17:48 +00:00
|
|
|
}
|
|
|
|
|
2023-12-17 23:44:56 +00:00
|
|
|
/**
|
|
|
|
* Go through the window stack and calculate some parameters for rendering.
|
|
|
|
*
|
|
|
|
* @return whether the operation succeeded
|
|
|
|
*/
|
|
|
|
static bool paint_preprocess(session_t *ps, bool *fade_running, bool *animation,
|
|
|
|
struct managed_win **out_bottom) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// XXX need better, more general name for `fade_running`. It really
|
|
|
|
// means if fade is still ongoing after the current frame is rendered
|
2019-04-17 22:14:45 +00:00
|
|
|
struct managed_win *bottom = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
*fade_running = false;
|
2022-08-12 12:44:14 +00:00
|
|
|
*animation = false;
|
2023-12-17 23:44:56 +00:00
|
|
|
*out_bottom = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Fading step calculation
|
2024-03-19 16:44:02 +00:00
|
|
|
unsigned int steps = 0L;
|
2019-03-10 12:34:37 +00:00
|
|
|
auto now = get_time_ms();
|
|
|
|
if (ps->fade_time) {
|
|
|
|
assert(now >= ps->fade_time);
|
2024-03-19 16:44:02 +00:00
|
|
|
auto raw_steps = (now - ps->fade_time) / ps->o.fade_delta;
|
|
|
|
assert(raw_steps <= UINT_MAX);
|
|
|
|
steps = (unsigned int)raw_steps;
|
|
|
|
ps->fade_time += raw_steps * ps->o.fade_delta;
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
|
|
|
// Reset fade_time if unset
|
|
|
|
ps->fade_time = get_time_ms();
|
|
|
|
steps = 0L;
|
|
|
|
}
|
|
|
|
|
2022-08-12 12:44:14 +00:00
|
|
|
// First, let's process fading, and animated shaders
|
|
|
|
// TODO(yshui) check if a window is fully obscured, and if we don't need to
|
|
|
|
// process fading or animation for it.
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
const winmode_t mode_old = w->mode;
|
|
|
|
const bool was_painted = w->to_paint;
|
|
|
|
|
|
|
|
if (win_should_dim(ps, w) != w->dim) {
|
|
|
|
w->dim = win_should_dim(ps, w);
|
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
|
|
|
|
2022-08-12 12:44:14 +00:00
|
|
|
if (w->fg_shader && (w->fg_shader->attributes & SHADER_ATTRIBUTE_ANIMATED)) {
|
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
*animation = true;
|
|
|
|
}
|
|
|
|
|
2019-03-27 15:30:37 +00:00
|
|
|
// Add window to damaged area if its opacity changes
|
|
|
|
// If was_painted == false, and to_paint is also false, we don't care
|
|
|
|
// If was_painted == false, but to_paint is true, damage will be added in
|
|
|
|
// the loop below
|
2024-03-21 18:01:02 +00:00
|
|
|
if (was_painted && w->number_of_animations != 0) {
|
2019-03-27 15:30:37 +00:00
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
|
|
|
|
2024-03-19 16:44:02 +00:00
|
|
|
// Run fading
|
2024-03-22 02:41:48 +00:00
|
|
|
if (run_fade(&w, steps)) {
|
2024-03-19 16:44:02 +00:00
|
|
|
*fade_running = true;
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
if (win_has_frame(w)) {
|
|
|
|
w->frame_opacity = ps->o.frame_opacity;
|
|
|
|
} else {
|
|
|
|
w->frame_opacity = 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update window mode
|
|
|
|
w->mode = win_calc_mode(w);
|
|
|
|
|
|
|
|
// Destroy all reg_ignore above when frame opaque state changes on
|
|
|
|
// SOLID mode
|
|
|
|
if (was_painted && w->mode != mode_old) {
|
|
|
|
w->reg_ignore_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Opacity will not change, from now on.
|
|
|
|
rc_region_t *last_reg_ignore = rc_region_new();
|
|
|
|
|
|
|
|
bool unredir_possible = false;
|
2019-03-25 00:56:34 +00:00
|
|
|
// Track whether it's the highest window to paint
|
2019-03-10 12:34:37 +00:00
|
|
|
bool is_highest = true;
|
|
|
|
bool reg_ignore_valid = true;
|
2024-04-01 23:59:18 +00:00
|
|
|
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
__label__ skip_window;
|
|
|
|
bool to_paint = true;
|
|
|
|
// w->to_paint remembers whether this window is painted last time
|
|
|
|
const bool was_painted = w->to_paint;
|
2024-03-19 16:44:02 +00:00
|
|
|
const double window_opacity = animatable_get(&w->opacity);
|
2024-03-22 02:41:48 +00:00
|
|
|
const double blur_opacity = animatable_get(&w->blur_opacity);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Destroy reg_ignore if some window above us invalidated it
|
2019-03-25 00:56:34 +00:00
|
|
|
if (!reg_ignore_valid) {
|
2019-03-10 12:34:37 +00:00
|
|
|
rc_region_unref(&w->reg_ignore);
|
2019-03-25 00:56:34 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// log_trace("%d %d %s", w->a.map_state, w->ever_damaged, w->name);
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("Checking whether window %#010x (%s) should be painted",
|
|
|
|
w->base.id, w->name);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Give up if it's not damaged or invisible, or it's unmapped and its
|
|
|
|
// pixmap is gone (for example due to a ConfigureNotify), or when it's
|
|
|
|
// excluded
|
2024-04-01 23:59:18 +00:00
|
|
|
if ((w->state == WSTATE_UNMAPPED || w->state == WSTATE_DESTROYED) &&
|
|
|
|
w->number_of_animations == 0) {
|
2024-03-22 02:41:48 +00:00
|
|
|
if (window_opacity != 0 || blur_opacity != 0) {
|
|
|
|
log_warn("Window %#010x (%s) is unmapped but still has "
|
|
|
|
"opacity",
|
|
|
|
w->base.id, w->name);
|
|
|
|
}
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- is unmapped");
|
|
|
|
to_paint = false;
|
2024-01-21 17:27:05 +00:00
|
|
|
} else if (unlikely(ps->debug_window != XCB_NONE) &&
|
|
|
|
(w->base.id == ps->debug_window ||
|
|
|
|
w->client_win == ps->debug_window)) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- is the debug window");
|
2020-12-04 02:53:43 +00:00
|
|
|
to_paint = false;
|
2024-03-22 02:41:48 +00:00
|
|
|
} else if (!w->ever_damaged) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- has not received any damages");
|
2020-12-04 02:53:43 +00:00
|
|
|
to_paint = false;
|
|
|
|
} else if (unlikely(w->g.x + w->g.width < 1 || w->g.y + w->g.height < 1 ||
|
|
|
|
w->g.x >= ps->root_width || w->g.y >= ps->root_height)) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- is positioned outside of the screen");
|
2020-12-04 02:53:43 +00:00
|
|
|
to_paint = false;
|
2024-03-22 02:41:48 +00:00
|
|
|
} else if (unlikely(window_opacity * MAX_ALPHA < 1 &&
|
|
|
|
(!w->blur_background || blur_opacity * MAX_ALPHA < 1))) {
|
|
|
|
// For consistency, even a window has 0 opacity, we would still
|
|
|
|
// blur its background. (unless it's background is not blurred, or
|
|
|
|
// the blur opacity is 0)
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- has 0 opacity");
|
2019-03-10 12:34:37 +00:00
|
|
|
to_paint = false;
|
2020-12-04 02:53:43 +00:00
|
|
|
} else if (w->paint_excluded) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- is excluded from painting");
|
2019-07-07 15:15:26 +00:00
|
|
|
to_paint = false;
|
2020-12-04 02:53:43 +00:00
|
|
|
} else if (unlikely((w->flags & WIN_FLAGS_IMAGE_ERROR) != 0)) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- has image errors");
|
2019-03-17 03:04:23 +00:00
|
|
|
to_paint = false;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
// log_trace("%s %d %d %d", w->name, to_paint, w->opacity,
|
|
|
|
// w->paint_excluded);
|
|
|
|
|
|
|
|
// Add window to damaged area if its painting status changes
|
|
|
|
// or opacity changes
|
|
|
|
if (to_paint != was_painted) {
|
|
|
|
w->reg_ignore_valid = false;
|
|
|
|
add_damage_from_win(ps, w);
|
|
|
|
}
|
|
|
|
|
2019-04-14 00:01:44 +00:00
|
|
|
// to_paint will never change after this point
|
|
|
|
if (!to_paint) {
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- will not be painted");
|
2019-03-10 12:34:37 +00:00
|
|
|
goto skip_window;
|
2019-04-14 00:01:44 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-01-21 17:22:31 +00:00
|
|
|
log_trace("|- will be painted");
|
|
|
|
log_verbose("Window %#010x (%s) will be painted", w->base.id, w->name);
|
2020-12-04 02:53:43 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Calculate shadow opacity
|
2024-03-19 16:44:02 +00:00
|
|
|
w->shadow_opacity = ps->o.shadow_opacity * window_opacity * ps->o.frame_opacity;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Generate ignore region for painting to reduce GPU load
|
2019-04-14 00:01:44 +00:00
|
|
|
if (!w->reg_ignore) {
|
2019-03-10 12:34:37 +00:00
|
|
|
w->reg_ignore = rc_region_ref(last_reg_ignore);
|
2019-04-14 00:01:44 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-11-30 21:24:22 +00:00
|
|
|
// If the window is solid, or we enabled clipping for transparent windows,
|
|
|
|
// we add the window region to the ignored region
|
2019-03-10 12:34:37 +00:00
|
|
|
// Otherwise last_reg_ignore shouldn't change
|
2019-11-30 21:24:22 +00:00
|
|
|
if ((w->mode != WMODE_TRANS && !ps->o.force_win_blend) ||
|
2021-12-01 22:51:57 +00:00
|
|
|
(ps->o.transparent_clipping && !w->transparent_clipping_excluded)) {
|
2019-10-24 18:36:19 +00:00
|
|
|
// w->mode == WMODE_SOLID or WMODE_FRAME_TRANS
|
2019-03-10 12:34:37 +00:00
|
|
|
region_t *tmp = rc_region_new();
|
2019-10-24 18:36:19 +00:00
|
|
|
if (w->mode == WMODE_SOLID) {
|
2020-09-05 14:43:25 +00:00
|
|
|
*tmp =
|
|
|
|
win_get_bounding_shape_global_without_corners_by_val(w);
|
2019-10-24 18:36:19 +00:00
|
|
|
} else {
|
|
|
|
// w->mode == WMODE_FRAME_TRANS
|
2020-09-05 14:43:25 +00:00
|
|
|
win_get_region_noframe_local_without_corners(w, tmp);
|
2019-03-10 12:34:37 +00:00
|
|
|
pixman_region32_intersect(tmp, tmp, &w->bounding_shape);
|
|
|
|
pixman_region32_translate(tmp, w->g.x, w->g.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
pixman_region32_union(tmp, tmp, last_reg_ignore);
|
|
|
|
rc_region_unref(&last_reg_ignore);
|
|
|
|
last_reg_ignore = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// (Un)redirect screen
|
|
|
|
// We could definitely unredirect the screen when there's no window to
|
|
|
|
// paint, but this is typically unnecessary, may cause flickering when
|
|
|
|
// fading is enabled, and could create inconsistency when the wallpaper
|
|
|
|
// is not correctly set.
|
|
|
|
if (ps->o.unredir_if_possible && is_highest) {
|
2019-07-13 11:29:39 +00:00
|
|
|
if (w->mode == WMODE_SOLID && !ps->o.force_win_blend &&
|
2024-02-14 18:54:04 +00:00
|
|
|
w->is_fullscreen && !w->unredir_if_possible_excluded) {
|
2019-03-10 12:34:37 +00:00
|
|
|
unredir_possible = true;
|
2019-07-13 11:29:39 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2020-01-18 18:00:35 +00:00
|
|
|
// Unredirect screen if some window is requesting compositor bypass, even
|
|
|
|
// if that window is not on the top.
|
|
|
|
if (ps->o.unredir_if_possible && win_is_bypassing_compositor(ps, w) &&
|
|
|
|
!w->unredir_if_possible_excluded) {
|
|
|
|
// Here we deviate from EWMH a bit. EWMH says we must not
|
|
|
|
// unredirect the screen if the window requesting bypassing would
|
|
|
|
// look different after unredirecting. Instead we always follow
|
|
|
|
// the request.
|
|
|
|
unredir_possible = true;
|
|
|
|
}
|
|
|
|
|
2019-04-17 22:14:45 +00:00
|
|
|
w->prev_trans = bottom;
|
2019-07-25 23:00:51 +00:00
|
|
|
if (bottom) {
|
|
|
|
w->stacking_rank = bottom->stacking_rank + 1;
|
|
|
|
} else {
|
|
|
|
w->stacking_rank = 0;
|
|
|
|
}
|
2019-04-17 22:14:45 +00:00
|
|
|
bottom = w;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// If the screen is not redirected and the window has redir_ignore set,
|
|
|
|
// this window should not cause the screen to become redirected
|
|
|
|
if (!(ps->o.wintype_option[w->window_type].redir_ignore && !ps->redirected)) {
|
|
|
|
is_highest = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
skip_window:
|
|
|
|
reg_ignore_valid = reg_ignore_valid && w->reg_ignore_valid;
|
|
|
|
w->reg_ignore_valid = true;
|
|
|
|
|
2024-04-01 23:59:18 +00:00
|
|
|
if (w->state == WSTATE_DESTROYED && w->number_of_animations == 0) {
|
|
|
|
// the window should be destroyed because it was destroyed
|
|
|
|
// by X server and now its animations are finished
|
|
|
|
destroy_win_finish(ps, &w->base);
|
|
|
|
w = NULL;
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Avoid setting w->to_paint if w is freed
|
|
|
|
if (w) {
|
|
|
|
w->to_paint = to_paint;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rc_region_unref(&last_reg_ignore);
|
|
|
|
|
|
|
|
// If possible, unredirect all windows and stop painting
|
2019-03-24 04:47:25 +00:00
|
|
|
if (ps->o.redirected_force != UNSET) {
|
2019-03-10 12:34:37 +00:00
|
|
|
unredir_possible = !ps->o.redirected_force;
|
2019-03-24 04:47:25 +00:00
|
|
|
} else if (ps->o.unredir_if_possible && is_highest && !ps->redirected) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// If there's no window to paint, and the screen isn't redirected,
|
|
|
|
// don't redirect it.
|
|
|
|
unredir_possible = true;
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
if (unredir_possible) {
|
|
|
|
if (ps->redirected) {
|
2019-09-28 22:43:36 +00:00
|
|
|
if (!ps->o.unredir_if_possible_delay || ps->tmout_unredir_hit) {
|
|
|
|
unredirect(ps);
|
|
|
|
} else if (!ev_is_active(&ps->unredir_timer)) {
|
2019-03-30 09:07:21 +00:00
|
|
|
ev_timer_set(
|
|
|
|
&ps->unredir_timer,
|
|
|
|
(double)ps->o.unredir_if_possible_delay / 1000.0, 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_timer_start(ps->loop, &ps->unredir_timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ev_timer_stop(ps->loop, &ps->unredir_timer);
|
2019-03-24 04:47:25 +00:00
|
|
|
if (!ps->redirected) {
|
2019-09-28 22:43:36 +00:00
|
|
|
if (!redirect_start(ps)) {
|
2023-12-17 23:44:56 +00:00
|
|
|
return false;
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-17 23:44:56 +00:00
|
|
|
*out_bottom = bottom;
|
|
|
|
return true;
|
2018-12-15 21:11:41 +00:00
|
|
|
}
|
2013-03-25 03:36:39 +00:00
|
|
|
|
2019-03-17 13:44:26 +00:00
|
|
|
void root_damaged(session_t *ps) {
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->root_tile_paint.pixmap) {
|
|
|
|
free_root_tile(ps);
|
|
|
|
}
|
|
|
|
|
2019-03-24 15:40:37 +00:00
|
|
|
if (!ps->redirected) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-04-20 04:26:20 +00:00
|
|
|
if (ps->backend_data) {
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->root_image) {
|
|
|
|
ps->backend_data->ops->release_image(ps->backend_data, ps->root_image);
|
2022-12-30 03:16:46 +00:00
|
|
|
ps->root_image = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2023-06-29 04:39:36 +00:00
|
|
|
auto pixmap = x_get_root_back_pixmap(&ps->c, ps->atoms);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (pixmap != XCB_NONE) {
|
2024-02-01 03:25:38 +00:00
|
|
|
xcb_get_geometry_reply_t *r = xcb_get_geometry_reply(
|
|
|
|
ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
|
|
|
|
if (!r) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// We used to assume that pixmaps pointed by the root background
|
|
|
|
// pixmap atoms are owned by the root window and have the same
|
|
|
|
// depth and hence the same visual that we can use to bind them.
|
|
|
|
// However, some applications break this assumption, e.g. the
|
|
|
|
// Xfce's desktop manager xfdesktop that sets the _XROOTPMAP_ID
|
|
|
|
// atom to a pixmap owned by it that seems to always have 32 bpp
|
|
|
|
// depth when the common root window's depth is 24 bpp. So use the
|
|
|
|
// root window's visual only if the root background pixmap's depth
|
|
|
|
// matches the root window's depth. Otherwise, find a suitable
|
|
|
|
// visual for the root background pixmap's depth and use it.
|
|
|
|
//
|
|
|
|
// We can't obtain a suitable visual for the root background
|
|
|
|
// pixmap the same way as the win_bind_pixmap function because it
|
|
|
|
// requires a window and we have only a pixmap. We also can't not
|
|
|
|
// bind the root background pixmap in case of depth mismatch
|
|
|
|
// because some options rely on it's content, e.g.
|
|
|
|
// transparent-clipping.
|
|
|
|
xcb_visualid_t visual =
|
|
|
|
r->depth == ps->c.screen_info->root_depth
|
|
|
|
? ps->c.screen_info->root_visual
|
2024-02-14 18:11:31 +00:00
|
|
|
: x_get_visual_for_depth(ps->c.screen_info, r->depth);
|
2024-02-01 03:25:38 +00:00
|
|
|
free(r);
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->root_image = ps->backend_data->ops->bind_pixmap(
|
2024-02-01 03:25:38 +00:00
|
|
|
ps->backend_data, pixmap, x_get_visual_info(&ps->c, visual), false);
|
2022-01-25 00:15:08 +00:00
|
|
|
if (ps->root_image) {
|
|
|
|
ps->backend_data->ops->set_image_property(
|
|
|
|
ps->backend_data, IMAGE_PROPERTY_EFFECTIVE_SIZE,
|
|
|
|
ps->root_image, (int[]){ps->root_width, ps->root_height});
|
|
|
|
} else {
|
2024-02-01 03:25:38 +00:00
|
|
|
err:
|
2022-01-25 00:15:08 +00:00
|
|
|
log_error("Failed to bind root back pixmap");
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark screen damaged
|
|
|
|
force_repaint(ps);
|
2012-10-24 02:09:59 +00:00
|
|
|
}
|
|
|
|
|
2013-01-24 05:38:03 +00:00
|
|
|
/**
|
|
|
|
* Force a full-screen repaint.
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
void force_repaint(session_t *ps) {
|
|
|
|
assert(pixman_region32_not_empty(&ps->screen_reg));
|
|
|
|
queue_redraw(ps);
|
|
|
|
add_damage(ps, &ps->screen_reg);
|
2013-01-24 05:38:03 +00:00
|
|
|
}
|
|
|
|
|
Feature: #7: VSync
- Add VSync feature. 3 possible VSync methods available: "sw" (software,
not too reliable, but at least you have something to fallback to),
"drm" (using DRM_IOCTL_WAIT_VBLANK, should work only on DRI drivers),
"opengl" (using SGI_swap_control extension OpenGL, might work on more
drivers than the DRM method). "sw" and "opengl" are briefly tested,
"drm" received utterly no test (because I use the nVidia binary blob).
They are enabled with "--vsync sw" / "--vsync drm" / "--vsync opengl".
- Add --refresh-rate to let user specify a refresh rate for software
VSync, in case the automatic refresh rate detection does not work
well.
- Seemingly the automatic refresh rate detection using X RandR in
software VSync detects refresh rate incorrectly. Need further investigation.
- Fix a few bugs in fading timing.
- Add a workaround for client window detection on Fluxbox, as Fluxbox
(incorrectly?) sets the override-redirect flag upon all frame
windows.
- Software VSync adds dependency on librt (a part of glibc) for
nanosecond-level timing functions, and libXrandr for automatic refresh
rate detection; DRM VSync adds dependency on libdrm to use its drm.h,
but does not link to libdrm; OpenGL VSync adds dependency on libGL.
- Print timing information on DEBUG_REPAINT.
2012-10-08 02:20:01 +00:00
|
|
|
/**
|
2020-10-22 00:39:51 +00:00
|
|
|
* Setup window properties, then register us with the compositor selection (_NET_WM_CM_S)
|
2020-03-11 19:15:26 +00:00
|
|
|
*
|
|
|
|
* @return 0 if success, 1 if compositor already running, -1 if error.
|
Feature: #7: VSync
- Add VSync feature. 3 possible VSync methods available: "sw" (software,
not too reliable, but at least you have something to fallback to),
"drm" (using DRM_IOCTL_WAIT_VBLANK, should work only on DRI drivers),
"opengl" (using SGI_swap_control extension OpenGL, might work on more
drivers than the DRM method). "sw" and "opengl" are briefly tested,
"drm" received utterly no test (because I use the nVidia binary blob).
They are enabled with "--vsync sw" / "--vsync drm" / "--vsync opengl".
- Add --refresh-rate to let user specify a refresh rate for software
VSync, in case the automatic refresh rate detection does not work
well.
- Seemingly the automatic refresh rate detection using X RandR in
software VSync detects refresh rate incorrectly. Need further investigation.
- Fix a few bugs in fading timing.
- Add a workaround for client window detection on Fluxbox, as Fluxbox
(incorrectly?) sets the override-redirect flag upon all frame
windows.
- Software VSync adds dependency on librt (a part of glibc) for
nanosecond-level timing functions, and libXrandr for automatic refresh
rate detection; DRM VSync adds dependency on libdrm to use its drm.h,
but does not link to libdrm; OpenGL VSync adds dependency on libGL.
- Print timing information on DEBUG_REPAINT.
2012-10-08 02:20:01 +00:00
|
|
|
*/
|
2020-03-11 19:15:26 +00:00
|
|
|
static int register_cm(session_t *ps) {
|
2019-03-10 12:34:37 +00:00
|
|
|
assert(!ps->reg_win);
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->reg_win = x_new_id(&ps->c);
|
2019-03-30 09:07:21 +00:00
|
|
|
auto e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_create_window_checked(ps->c.c, XCB_COPY_FROM_PARENT, ps->reg_win,
|
|
|
|
ps->c.screen_info->root, 0, 0, 1, 1, 0, XCB_NONE,
|
|
|
|
ps->c.screen_info->root_visual, 0, NULL));
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-30 09:07:21 +00:00
|
|
|
if (e) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("Failed to create window.");
|
2019-03-30 09:07:21 +00:00
|
|
|
free(e);
|
2020-03-11 19:15:26 +00:00
|
|
|
return -1;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2020-10-22 00:39:51 +00:00
|
|
|
const xcb_atom_t prop_atoms[] = {
|
|
|
|
ps->atoms->aWM_NAME,
|
|
|
|
ps->atoms->a_NET_WM_NAME,
|
|
|
|
ps->atoms->aWM_ICON_NAME,
|
|
|
|
};
|
|
|
|
|
|
|
|
const bool prop_is_utf8[] = {false, true, false};
|
|
|
|
|
|
|
|
// Set names and classes
|
|
|
|
for (size_t i = 0; i < ARR_SIZE(prop_atoms); i++) {
|
|
|
|
e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_change_property_checked(
|
|
|
|
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win, prop_atoms[i],
|
|
|
|
prop_is_utf8[i] ? ps->atoms->aUTF8_STRING : XCB_ATOM_STRING,
|
|
|
|
8, strlen("picom"), "picom"));
|
2020-10-22 00:39:51 +00:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "Failed to set window property %d",
|
|
|
|
prop_atoms[i]);
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const char picom_class[] = "picom\0picom";
|
|
|
|
e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_change_property_checked(ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
|
|
|
ps->atoms->aWM_CLASS, XCB_ATOM_STRING, 8,
|
|
|
|
ARR_SIZE(picom_class), picom_class));
|
2020-10-22 00:39:51 +00:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "Failed to set the WM_CLASS property");
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set WM_CLIENT_MACHINE. As per EWMH, because we set _NET_WM_PID, we must also
|
|
|
|
// set WM_CLIENT_MACHINE.
|
2019-03-10 12:34:37 +00:00
|
|
|
{
|
2023-12-17 08:55:23 +00:00
|
|
|
auto const hostname_max = (unsigned long)sysconf(_SC_HOST_NAME_MAX);
|
2020-10-25 03:13:03 +00:00
|
|
|
char *hostname = malloc(hostname_max);
|
|
|
|
|
|
|
|
if (gethostname(hostname, hostname_max) == 0) {
|
2020-10-22 00:39:51 +00:00
|
|
|
e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_change_property_checked(
|
|
|
|
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
|
|
|
ps->atoms->aWM_CLIENT_MACHINE, XCB_ATOM_STRING,
|
|
|
|
8, (uint32_t)strlen(hostname), hostname));
|
2020-10-22 00:39:51 +00:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "Failed to set the WM_CLIENT_MACHINE"
|
|
|
|
" property");
|
|
|
|
free(e);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_error_errno("Failed to get hostname");
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2020-10-25 03:13:03 +00:00
|
|
|
|
|
|
|
free(hostname);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set _NET_WM_PID
|
|
|
|
{
|
2019-03-30 09:07:21 +00:00
|
|
|
auto pid = getpid();
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_change_property(ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
2019-06-09 18:25:34 +00:00
|
|
|
ps->atoms->a_NET_WM_PID, XCB_ATOM_CARDINAL, 32, 1, &pid);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set COMPTON_VERSION
|
2024-02-15 20:11:42 +00:00
|
|
|
e = xcb_request_check(ps->c.c, xcb_change_property_checked(
|
|
|
|
ps->c.c, XCB_PROP_MODE_REPLACE, ps->reg_win,
|
|
|
|
ps->atoms->aCOMPTON_VERSION, XCB_ATOM_STRING, 8,
|
|
|
|
(uint32_t)strlen(PICOM_VERSION), PICOM_VERSION));
|
2020-10-21 02:06:15 +00:00
|
|
|
if (e) {
|
|
|
|
log_error_x_error(e, "Failed to set COMPTON_VERSION.");
|
|
|
|
free(e);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Acquire X Selection _NET_WM_CM_S?
|
|
|
|
if (!ps->o.no_x_selection) {
|
2019-05-06 00:24:38 +00:00
|
|
|
const char register_prop[] = "_NET_WM_CM_S";
|
2019-03-30 09:07:21 +00:00
|
|
|
xcb_atom_t atom;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-05-06 00:24:38 +00:00
|
|
|
char *buf = NULL;
|
2023-06-29 04:39:36 +00:00
|
|
|
if (asprintf(&buf, "%s%d", register_prop, ps->c.screen) < 0) {
|
2019-05-06 00:24:38 +00:00
|
|
|
log_fatal("Failed to allocate memory");
|
2020-03-11 19:15:26 +00:00
|
|
|
return -1;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2024-02-18 00:40:07 +00:00
|
|
|
atom = get_atom_with_nul(ps->atoms, buf, ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
free(buf);
|
|
|
|
|
|
|
|
xcb_get_selection_owner_reply_t *reply = xcb_get_selection_owner_reply(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_get_selection_owner(ps->c.c, atom), NULL);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
if (reply && reply->owner != XCB_NONE) {
|
2020-03-11 19:15:26 +00:00
|
|
|
// Another compositor already running
|
2019-03-10 12:34:37 +00:00
|
|
|
free(reply);
|
2020-03-11 19:15:26 +00:00
|
|
|
return 1;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
free(reply);
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_set_selection_owner(ps->c.c, ps->reg_win, atom, 0);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 19:15:26 +00:00
|
|
|
return 0;
|
2005-10-07 00:08:02 +00:00
|
|
|
}
|
|
|
|
|
2013-11-09 13:38:31 +00:00
|
|
|
/**
|
|
|
|
* Write PID to a file.
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static inline bool write_pid(session_t *ps) {
|
2020-10-22 00:39:51 +00:00
|
|
|
if (!ps->o.write_pid_path) {
|
2019-03-10 12:34:37 +00:00
|
|
|
return true;
|
2020-10-22 00:39:51 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
FILE *f = fopen(ps->o.write_pid_path, "w");
|
|
|
|
if (unlikely(!f)) {
|
|
|
|
log_error("Failed to write PID to \"%s\".", ps->o.write_pid_path);
|
|
|
|
return false;
|
|
|
|
}
|
2013-11-09 13:38:31 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
fprintf(f, "%ld\n", (long)getpid());
|
|
|
|
fclose(f);
|
2013-11-09 13:38:31 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
return true;
|
2013-11-09 13:38:31 +00:00
|
|
|
}
|
|
|
|
|
2012-10-24 02:09:59 +00:00
|
|
|
/**
|
|
|
|
* Initialize X composite overlay window.
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static bool init_overlay(session_t *ps) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_composite_get_overlay_window_reply_t *reply = xcb_composite_get_overlay_window_reply(
|
|
|
|
ps->c.c, xcb_composite_get_overlay_window(ps->c.c, ps->c.screen_info->root), NULL);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (reply) {
|
|
|
|
ps->overlay = reply->overlay_win;
|
|
|
|
free(reply);
|
|
|
|
} else {
|
|
|
|
ps->overlay = XCB_NONE;
|
|
|
|
}
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->overlay != XCB_NONE) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Set window region of the overlay window, code stolen from
|
|
|
|
// compiz-0.8.8
|
2023-06-29 04:39:36 +00:00
|
|
|
if (!XCB_AWAIT_VOID(xcb_shape_mask, ps->c.c, XCB_SHAPE_SO_SET,
|
2019-04-27 13:15:40 +00:00
|
|
|
XCB_SHAPE_SK_BOUNDING, ps->overlay, 0, 0, 0)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("Failed to set the bounding shape of overlay, giving "
|
|
|
|
"up.");
|
2019-04-27 13:15:40 +00:00
|
|
|
return false;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2023-06-29 04:39:36 +00:00
|
|
|
if (!XCB_AWAIT_VOID(xcb_shape_rectangles, ps->c.c, XCB_SHAPE_SO_SET,
|
2019-03-10 12:34:37 +00:00
|
|
|
XCB_SHAPE_SK_INPUT, XCB_CLIP_ORDERING_UNSORTED,
|
2019-04-27 13:15:40 +00:00
|
|
|
ps->overlay, 0, 0, 0, NULL)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("Failed to set the input shape of overlay, giving up.");
|
2019-04-27 13:15:40 +00:00
|
|
|
return false;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Listen to Expose events on the overlay
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_change_window_attributes(ps->c.c, ps->overlay, XCB_CW_EVENT_MASK,
|
2019-03-10 12:34:37 +00:00
|
|
|
(const uint32_t[]){XCB_EVENT_MASK_EXPOSURE});
|
|
|
|
|
|
|
|
// Retrieve DamageNotify on root window if we are painting on an
|
|
|
|
// overlay
|
|
|
|
// root_damage = XDamageCreate(ps->dpy, root, XDamageReportNonEmpty);
|
|
|
|
|
2019-09-28 22:43:36 +00:00
|
|
|
// Unmap the overlay, we will map it when needed in redirect_start
|
2023-06-29 04:39:36 +00:00
|
|
|
XCB_AWAIT_VOID(xcb_unmap_window, ps->c.c, ps->overlay);
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
|
|
|
log_error("Cannot get X Composite overlay window. Falling "
|
|
|
|
"back to painting on root window.");
|
|
|
|
}
|
|
|
|
log_debug("overlay = %#010x", ps->overlay);
|
|
|
|
|
2019-04-27 13:15:40 +00:00
|
|
|
return true;
|
2012-10-24 02:09:59 +00:00
|
|
|
}
|
|
|
|
|
2019-07-07 15:12:17 +00:00
|
|
|
static bool init_debug_window(session_t *ps) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_colormap_t colormap = x_new_id(&ps->c);
|
|
|
|
ps->debug_window = x_new_id(&ps->c);
|
2019-07-07 15:12:17 +00:00
|
|
|
|
|
|
|
auto err = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_create_colormap_checked(ps->c.c, XCB_COLORMAP_ALLOC_NONE,
|
|
|
|
colormap, ps->c.screen_info->root,
|
|
|
|
ps->c.screen_info->root_visual));
|
2019-07-07 15:12:17 +00:00
|
|
|
if (err) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_create_window_checked(
|
|
|
|
ps->c.c, (uint8_t)ps->c.screen_info->root_depth,
|
|
|
|
ps->debug_window, ps->c.screen_info->root, 0, 0,
|
|
|
|
to_u16_checked(ps->root_width), to_u16_checked(ps->root_height),
|
|
|
|
0, XCB_WINDOW_CLASS_INPUT_OUTPUT, ps->c.screen_info->root_visual,
|
|
|
|
XCB_CW_COLORMAP, (uint32_t[]){colormap, 0}));
|
2019-07-07 15:12:17 +00:00
|
|
|
if (err) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
err = xcb_request_check(ps->c.c, xcb_map_window_checked(ps->c.c, ps->debug_window));
|
2019-07-07 15:12:17 +00:00
|
|
|
if (err) {
|
|
|
|
goto err_out;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
err_out:
|
|
|
|
free(err);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
xcb_window_t session_get_target_window(session_t *ps) {
|
|
|
|
if (ps->o.debug_mode) {
|
|
|
|
return ps->debug_window;
|
|
|
|
}
|
2023-06-29 04:39:36 +00:00
|
|
|
return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root;
|
2019-07-07 15:12:17 +00:00
|
|
|
}
|
|
|
|
|
2024-03-23 14:01:46 +00:00
|
|
|
#ifdef CONFIG_DBUS
|
|
|
|
struct cdbus_data *session_get_cdbus(struct session *ps) {
|
|
|
|
return ps->dbus_data;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2020-03-11 19:15:26 +00:00
|
|
|
uint8_t session_redirection_mode(session_t *ps) {
|
2019-11-10 05:07:03 +00:00
|
|
|
if (ps->o.debug_mode) {
|
|
|
|
// If the backend is not rendering to the screen, we don't need to
|
|
|
|
// take over the screen.
|
2022-08-21 05:00:43 +00:00
|
|
|
assert(!ps->o.legacy_backends);
|
2019-11-10 05:07:03 +00:00
|
|
|
return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
|
|
|
|
}
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends && !backend_list[ps->o.backend]->present) {
|
2019-11-10 05:07:03 +00:00
|
|
|
// if the backend doesn't render anything, we don't need to take over the
|
|
|
|
// screen.
|
2019-08-04 15:46:09 +00:00
|
|
|
return XCB_COMPOSITE_REDIRECT_AUTOMATIC;
|
|
|
|
}
|
|
|
|
return XCB_COMPOSITE_REDIRECT_MANUAL;
|
|
|
|
}
|
|
|
|
|
2012-11-09 13:44:02 +00:00
|
|
|
/**
|
|
|
|
* Redirect all windows.
|
2019-02-25 00:07:12 +00:00
|
|
|
*
|
|
|
|
* @return whether the operation succeeded or not
|
2012-11-09 13:44:02 +00:00
|
|
|
*/
|
2019-09-28 22:43:36 +00:00
|
|
|
static bool redirect_start(session_t *ps) {
|
2019-03-24 04:47:25 +00:00
|
|
|
assert(!ps->redirected);
|
|
|
|
log_debug("Redirecting the screen.");
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
// Map overlay window. Done firstly according to this:
|
|
|
|
// https://bugzilla.gnome.org/show_bug.cgi?id=597014
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->overlay != XCB_NONE) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_map_window(ps->c.c, ps->overlay);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
bool success = XCB_AWAIT_VOID(xcb_composite_redirect_subwindows, ps->c.c,
|
|
|
|
ps->c.screen_info->root, session_redirection_mode(ps));
|
2020-03-27 21:02:38 +00:00
|
|
|
if (!success) {
|
|
|
|
log_fatal("Another composite manager is already running "
|
|
|
|
"(and does not handle _NET_WM_CM_Sn correctly)");
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-02-14 17:30:39 +00:00
|
|
|
xcb_aux_sync(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
if (!initialize_backend(ps)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
2019-04-20 04:26:20 +00:00
|
|
|
assert(ps->backend_data);
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.count = ps->backend_data->ops->max_buffer_age;
|
2019-03-24 04:47:25 +00:00
|
|
|
} else {
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.count = maximum_buffer_age(ps);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.damages = ccalloc(ps->damage_ring.count, region_t);
|
|
|
|
ps->damage_ring.cursor = ps->damage_ring.count - 1;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-24 03:22:58 +00:00
|
|
|
for (int i = 0; i < ps->damage_ring.count; i++) {
|
|
|
|
pixman_region32_init(&ps->damage_ring.damages[i]);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-12-19 11:07:55 +00:00
|
|
|
ps->frame_pacing = !ps->o.no_frame_pacing && ps->o.vsync;
|
2023-06-10 13:28:43 +00:00
|
|
|
if ((ps->o.legacy_backends || ps->o.benchmark || !ps->backend_data->ops->last_render_time) &&
|
|
|
|
ps->frame_pacing) {
|
2022-12-13 20:56:11 +00:00
|
|
|
// Disable frame pacing if we are using a legacy backend or if we are in
|
|
|
|
// benchmark mode, or if the backend doesn't report render time
|
|
|
|
log_info("Disabling frame pacing.");
|
|
|
|
ps->frame_pacing = false;
|
|
|
|
}
|
|
|
|
|
2023-12-19 10:29:59 +00:00
|
|
|
// Re-detect driver since we now have a backend
|
|
|
|
ps->drivers = detect_driver(ps->c.c, ps->backend_data, ps->c.screen_info->root);
|
|
|
|
apply_driver_workarounds(ps, ps->drivers);
|
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
if (ps->present_exists && ps->frame_pacing) {
|
|
|
|
// Initialize rendering and frame timing statistics, and frame pacing
|
|
|
|
// states.
|
|
|
|
ps->last_msc_instant = 0;
|
2022-12-13 20:31:32 +00:00
|
|
|
ps->last_msc = 0;
|
2023-06-10 13:08:49 +00:00
|
|
|
ps->last_schedule_delay = 0;
|
|
|
|
render_statistics_reset(&ps->render_stats);
|
2023-12-19 10:29:59 +00:00
|
|
|
enum vblank_scheduler_type scheduler_type =
|
|
|
|
choose_vblank_scheduler(ps->drivers);
|
2023-12-19 10:29:31 +00:00
|
|
|
if (ps->o.debug_options.force_vblank_scheduler != LAST_VBLANK_SCHEDULER) {
|
|
|
|
scheduler_type =
|
|
|
|
(enum vblank_scheduler_type)ps->o.debug_options.force_vblank_scheduler;
|
|
|
|
}
|
2023-12-19 10:29:59 +00:00
|
|
|
log_info("Using vblank scheduler: %s.", vblank_scheduler_str[scheduler_type]);
|
2024-03-03 01:24:26 +00:00
|
|
|
ps->vblank_scheduler =
|
|
|
|
vblank_scheduler_new(ps->loop, &ps->c, session_get_target_window(ps),
|
|
|
|
scheduler_type, ps->o.use_realtime_scheduling);
|
2023-07-09 15:39:44 +00:00
|
|
|
if (!ps->vblank_scheduler) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
vblank_scheduler_schedule(ps->vblank_scheduler,
|
|
|
|
collect_vblank_interval_statistics, ps);
|
2022-12-13 20:56:11 +00:00
|
|
|
} else if (ps->frame_pacing) {
|
|
|
|
log_error("Present extension is not supported, frame pacing disabled.");
|
|
|
|
ps->frame_pacing = false;
|
2022-12-13 20:31:32 +00:00
|
|
|
}
|
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
// Must call XSync() here
|
2024-02-14 17:30:39 +00:00
|
|
|
xcb_aux_sync(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
ps->redirected = true;
|
2019-09-28 23:11:32 +00:00
|
|
|
ps->first_frame = true;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
root_damaged(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
// Repaint the whole screen
|
|
|
|
force_repaint(ps);
|
|
|
|
log_debug("Screen redirected.");
|
2019-03-10 12:34:37 +00:00
|
|
|
return true;
|
2012-11-09 13:44:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unredirect all windows.
|
|
|
|
*/
|
2019-09-28 22:43:36 +00:00
|
|
|
static void unredirect(session_t *ps) {
|
2019-03-24 04:47:25 +00:00
|
|
|
assert(ps->redirected);
|
|
|
|
log_debug("Unredirecting the screen.");
|
2019-03-17 16:08:31 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
destroy_backend(ps);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_composite_unredirect_subwindows(ps->c.c, ps->c.screen_info->root,
|
|
|
|
session_redirection_mode(ps));
|
2019-03-24 04:47:25 +00:00
|
|
|
// Unmap overlay window
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->overlay != XCB_NONE) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_unmap_window(ps->c.c, ps->overlay);
|
2020-08-30 13:10:26 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
// Free the damage ring
|
2024-03-24 03:22:58 +00:00
|
|
|
for (int i = 0; i < ps->damage_ring.count; ++i) {
|
|
|
|
pixman_region32_fini(&ps->damage_ring.damages[i]);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.count = 0;
|
|
|
|
free(ps->damage_ring.damages);
|
|
|
|
ps->damage_ring.cursor = 0;
|
|
|
|
ps->damage_ring.damages = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
if (ps->vblank_scheduler) {
|
|
|
|
vblank_scheduler_free(ps->vblank_scheduler);
|
|
|
|
ps->vblank_scheduler = NULL;
|
2022-12-13 20:31:32 +00:00
|
|
|
}
|
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
// Must call XSync() here
|
2024-02-14 17:30:39 +00:00
|
|
|
xcb_aux_sync(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
ps->redirected = false;
|
|
|
|
log_debug("Screen unredirected.");
|
2012-11-09 13:44:02 +00:00
|
|
|
}
|
|
|
|
|
2024-02-06 09:14:05 +00:00
|
|
|
/// Handle queued events before we go to sleep.
|
|
|
|
///
|
|
|
|
/// This function is called by ev_prepare watcher, which is called just before
|
|
|
|
/// the event loop goes to sleep. X damage events are incremental, which means
|
|
|
|
/// if we don't handle the ones X server already sent us, we won't get new ones.
|
|
|
|
/// And if we don't get new ones, we won't render, i.e. we would freeze. libxcb
|
|
|
|
/// keeps an internal queue of events, so we have to be 100% sure no events are
|
|
|
|
/// left in that queue before we go to sleep.
|
2019-07-25 01:27:02 +00:00
|
|
|
static void handle_queued_x_events(EV_P attr_unused, ev_prepare *w, int revents attr_unused) {
|
2019-03-10 12:34:37 +00:00
|
|
|
session_t *ps = session_ptr(w, event_check);
|
2024-02-06 09:14:05 +00:00
|
|
|
// Flush because if we go into sleep when there is still requests in the
|
|
|
|
// outgoing buffer, they will not be sent for an indefinite amount of
|
|
|
|
// time. Use XFlush here too, we might still use some Xlib functions
|
|
|
|
// because OpenGL.
|
|
|
|
//
|
|
|
|
// Also note, after we have flushed here, we won't flush again in this
|
|
|
|
// function before going into sleep. This is because `xcb_flush`/`XFlush`
|
|
|
|
// may _read_ more events from the server (yes, this is ridiculous, I
|
|
|
|
// know). And we can't have that, see the comments above this function.
|
|
|
|
//
|
|
|
|
// This means if functions called ev_handle need to send some events,
|
|
|
|
// they need to carefully make sure those events are flushed, one way or
|
|
|
|
// another.
|
|
|
|
XFlush(ps->c.dpy);
|
|
|
|
xcb_flush(ps->c.c);
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
if (ps->vblank_scheduler) {
|
|
|
|
vblank_handle_x_events(ps->vblank_scheduler);
|
|
|
|
}
|
2022-12-13 20:31:32 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_generic_event_t *ev;
|
2023-06-29 04:39:36 +00:00
|
|
|
while ((ev = xcb_poll_for_queued_event(ps->c.c))) {
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_handle(ps, ev);
|
|
|
|
free(ev);
|
|
|
|
};
|
2023-06-29 04:39:36 +00:00
|
|
|
int err = xcb_connection_has_error(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (err) {
|
|
|
|
log_fatal("X11 server connection broke (error %d)", err);
|
|
|
|
exit(1);
|
|
|
|
}
|
2018-10-02 19:22:26 +00:00
|
|
|
}
|
|
|
|
|
2019-04-19 19:53:52 +00:00
|
|
|
static void handle_new_windows(session_t *ps) {
|
2024-03-26 17:54:34 +00:00
|
|
|
list_foreach_safe(struct win, w, wm_stack_end(ps->wm), stack_neighbour) {
|
2019-04-19 19:53:52 +00:00
|
|
|
if (w->is_new) {
|
2024-03-24 02:12:25 +00:00
|
|
|
auto new_w = maybe_allocate_managed_win(ps, w);
|
|
|
|
if (new_w == w) {
|
2019-04-19 20:10:38 +00:00
|
|
|
continue;
|
|
|
|
}
|
2024-03-24 02:12:25 +00:00
|
|
|
|
|
|
|
assert(new_w->managed);
|
2024-03-26 17:54:34 +00:00
|
|
|
wm_stack_replace(ps->wm, w, new_w);
|
2024-03-24 02:12:25 +00:00
|
|
|
|
2019-04-19 20:10:38 +00:00
|
|
|
auto mw = (struct managed_win *)new_w;
|
|
|
|
if (mw->a.map_state == XCB_MAP_STATE_VIEWABLE) {
|
2020-11-28 22:48:01 +00:00
|
|
|
win_set_flags(mw, WIN_FLAGS_MAPPED);
|
2019-04-19 21:52:32 +00:00
|
|
|
|
|
|
|
// This window might be damaged before we called fill_win
|
2019-09-16 21:14:34 +00:00
|
|
|
// and created the damage handle. And there is no way for
|
2019-04-19 21:52:32 +00:00
|
|
|
// us to find out. So just blindly mark it damaged
|
|
|
|
mw->ever_damaged = true;
|
2019-04-19 20:10:38 +00:00
|
|
|
}
|
2024-03-24 02:12:25 +00:00
|
|
|
// Send D-Bus signal
|
|
|
|
if (ps->o.dbus) {
|
2024-03-23 14:01:46 +00:00
|
|
|
cdbus_ev_win_added(session_get_cdbus(ps), new_w);
|
2024-03-24 02:12:25 +00:00
|
|
|
}
|
2019-04-19 19:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
2019-05-22 21:38:22 +00:00
|
|
|
}
|
|
|
|
|
2019-09-20 00:59:25 +00:00
|
|
|
static void refresh_windows(session_t *ps) {
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
2020-08-27 16:44:58 +00:00
|
|
|
win_process_update_flags(ps, w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void refresh_images(session_t *ps) {
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed(w, wm_stack_end(ps->wm)) {
|
2020-08-27 16:44:58 +00:00
|
|
|
win_process_image_flags(ps, w);
|
2019-05-22 21:38:22 +00:00
|
|
|
}
|
2019-04-19 19:53:52 +00:00
|
|
|
}
|
|
|
|
|
2013-09-04 14:00:51 +00:00
|
|
|
/**
|
|
|
|
* Unredirection timeout callback.
|
|
|
|
*/
|
2019-07-25 01:27:02 +00:00
|
|
|
static void tmout_unredir_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
|
2019-03-10 12:34:37 +00:00
|
|
|
session_t *ps = session_ptr(w, unredir_timer);
|
|
|
|
ps->tmout_unredir_hit = true;
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
|
2019-07-25 01:27:02 +00:00
|
|
|
static void fade_timer_callback(EV_P attr_unused, ev_timer *w, int revents attr_unused) {
|
2023-06-10 13:08:49 +00:00
|
|
|
// TODO(yshui): do we still need the fade timer? we queue redraw automatically in
|
|
|
|
// draw_callback_impl if animation is running.
|
2019-03-10 12:34:37 +00:00
|
|
|
session_t *ps = session_ptr(w, fade_timer);
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
|
2019-09-19 16:20:18 +00:00
|
|
|
static void handle_pending_updates(EV_P_ struct session *ps) {
|
2019-05-22 21:38:22 +00:00
|
|
|
if (ps->pending_updates) {
|
|
|
|
log_debug("Delayed handling of events, entering critical section");
|
2023-06-29 04:39:36 +00:00
|
|
|
auto e = xcb_request_check(ps->c.c, xcb_grab_server_checked(ps->c.c));
|
2019-04-25 03:14:32 +00:00
|
|
|
if (e) {
|
2020-03-28 00:15:45 +00:00
|
|
|
log_fatal_x_error(e, "failed to grab x server");
|
2023-01-09 22:13:08 +00:00
|
|
|
free(e);
|
2019-10-23 18:27:30 +00:00
|
|
|
return quit(ps);
|
2019-04-25 03:14:32 +00:00
|
|
|
}
|
2019-04-19 19:53:52 +00:00
|
|
|
|
2019-08-07 23:46:56 +00:00
|
|
|
ps->server_grabbed = true;
|
|
|
|
|
2019-04-25 03:14:32 +00:00
|
|
|
// Catching up with X server
|
2019-07-25 01:27:02 +00:00
|
|
|
handle_queued_x_events(EV_A_ & ps->event_check, 0);
|
2019-04-19 19:53:52 +00:00
|
|
|
|
2024-03-25 18:05:46 +00:00
|
|
|
// Process new windows, and maybe allocate struct managed_win for them
|
2019-04-25 03:14:32 +00:00
|
|
|
handle_new_windows(ps);
|
2019-04-19 19:53:52 +00:00
|
|
|
|
2020-04-06 19:52:32 +00:00
|
|
|
// Handle screen changes
|
|
|
|
// This HAS TO be called before refresh_windows, as handle_root_flags
|
|
|
|
// could call configure_root, which will release images and mark them
|
|
|
|
// stale.
|
|
|
|
handle_root_flags(ps);
|
|
|
|
|
2024-03-25 18:05:46 +00:00
|
|
|
// Process window flags
|
2019-09-21 04:11:28 +00:00
|
|
|
refresh_windows(ps);
|
|
|
|
|
2024-03-26 13:41:25 +00:00
|
|
|
recheck_focus(ps);
|
2019-05-24 23:06:41 +00:00
|
|
|
|
2020-08-27 16:44:58 +00:00
|
|
|
// Process window flags (stale images)
|
|
|
|
refresh_images(ps);
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
e = xcb_request_check(ps->c.c, xcb_ungrab_server_checked(ps->c.c));
|
2019-04-25 03:14:32 +00:00
|
|
|
if (e) {
|
2020-03-28 00:15:45 +00:00
|
|
|
log_fatal_x_error(e, "failed to ungrab x server");
|
2023-01-09 22:13:08 +00:00
|
|
|
free(e);
|
2019-10-23 18:27:30 +00:00
|
|
|
return quit(ps);
|
2019-04-25 03:14:32 +00:00
|
|
|
}
|
2019-05-22 21:38:22 +00:00
|
|
|
|
2019-09-19 16:20:18 +00:00
|
|
|
ps->server_grabbed = false;
|
2019-05-22 21:38:22 +00:00
|
|
|
ps->pending_updates = false;
|
2019-09-19 16:20:18 +00:00
|
|
|
log_debug("Exited critical section");
|
2019-04-19 19:53:52 +00:00
|
|
|
}
|
2019-09-19 16:20:18 +00:00
|
|
|
}
|
|
|
|
|
2020-08-30 16:44:21 +00:00
|
|
|
static void draw_callback_impl(EV_P_ session_t *ps, int revents attr_unused) {
|
2023-07-09 15:39:44 +00:00
|
|
|
assert(!ps->backend_busy);
|
|
|
|
assert(ps->render_queued);
|
|
|
|
|
2023-06-10 22:12:31 +00:00
|
|
|
struct timespec now;
|
|
|
|
int64_t draw_callback_enter_us;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
|
|
|
|
draw_callback_enter_us = (now.tv_sec * 1000000LL + now.tv_nsec / 1000);
|
|
|
|
if (ps->next_render != 0) {
|
|
|
|
log_trace("Schedule delay: %" PRIi64 " us",
|
|
|
|
draw_callback_enter_us - (int64_t)ps->next_render);
|
|
|
|
}
|
|
|
|
|
2019-09-19 16:20:18 +00:00
|
|
|
handle_pending_updates(EV_A_ ps);
|
2019-04-19 19:53:52 +00:00
|
|
|
|
2023-06-10 22:12:31 +00:00
|
|
|
int64_t after_handle_pending_updates_us;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
after_handle_pending_updates_us = (now.tv_sec * 1000000LL + now.tv_nsec / 1000);
|
|
|
|
log_trace("handle_pending_updates took: %" PRIi64 " us",
|
|
|
|
after_handle_pending_updates_us - draw_callback_enter_us);
|
|
|
|
|
2019-09-28 23:11:32 +00:00
|
|
|
if (ps->first_frame) {
|
|
|
|
// If we are still rendering the first frame, if some of the windows are
|
|
|
|
// unmapped/destroyed during the above handle_pending_updates() call, they
|
|
|
|
// won't have pixmap before we rendered it, causing us to crash.
|
|
|
|
// But we will only render them if they are in fading. So we just skip
|
|
|
|
// fading for all windows here.
|
|
|
|
//
|
|
|
|
// Using foreach_safe here since skipping fading can cause window to be
|
|
|
|
// freed if it's destroyed.
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
2024-03-22 02:41:48 +00:00
|
|
|
win_skip_fading(w);
|
|
|
|
if (w->state == WSTATE_DESTROYED) {
|
|
|
|
destroy_win_finish(ps, &w->base);
|
|
|
|
}
|
2019-09-28 23:11:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->o.benchmark) {
|
|
|
|
if (ps->o.benchmark_wid) {
|
2024-03-26 17:54:34 +00:00
|
|
|
auto w = wm_find_managed(ps->wm, ps->o.benchmark_wid);
|
2019-04-17 22:14:45 +00:00
|
|
|
if (!w) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("Couldn't find specified benchmark window.");
|
|
|
|
exit(1);
|
|
|
|
}
|
2019-04-17 22:14:45 +00:00
|
|
|
add_damage_from_win(ps, w);
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
|
|
|
force_repaint(ps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-30 14:25:58 +00:00
|
|
|
/* TODO(yshui) Have a stripped down version of paint_preprocess that is used when
|
|
|
|
* screen is not redirected. its sole purpose should be to decide whether the
|
|
|
|
* screen should be redirected. */
|
2019-03-10 12:34:37 +00:00
|
|
|
bool fade_running = false;
|
2022-08-12 12:44:14 +00:00
|
|
|
bool animation = false;
|
2019-03-24 04:47:25 +00:00
|
|
|
bool was_redirected = ps->redirected;
|
2023-12-17 23:44:56 +00:00
|
|
|
struct managed_win *bottom = NULL;
|
|
|
|
if (!paint_preprocess(ps, &fade_running, &animation, &bottom)) {
|
2023-12-17 23:34:34 +00:00
|
|
|
log_fatal("Pre-render preparation has failed, exiting...");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2023-12-17 23:44:56 +00:00
|
|
|
ps->tmout_unredir_hit = false;
|
|
|
|
|
2019-03-24 04:47:25 +00:00
|
|
|
if (!was_redirected && ps->redirected) {
|
|
|
|
// paint_preprocess redirected the screen, which might change the state of
|
2019-09-20 00:59:25 +00:00
|
|
|
// some of the windows (e.g. the window image might become stale).
|
|
|
|
// so we rerun _draw_callback to make sure the rendering decision we make
|
|
|
|
// is up-to-date, and all the new flags got handled.
|
|
|
|
//
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) This is not ideal, we should try to avoid setting window
|
|
|
|
// flags in paint_preprocess.
|
2019-09-20 00:59:25 +00:00
|
|
|
log_debug("Re-run _draw_callback");
|
2020-08-30 16:44:21 +00:00
|
|
|
return draw_callback_impl(EV_A_ ps, revents);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Start/stop fade timer depends on whether window are fading
|
2019-03-24 04:47:25 +00:00
|
|
|
if (!fade_running && ev_is_active(&ps->fade_timer)) {
|
2019-07-25 01:27:02 +00:00
|
|
|
ev_timer_stop(EV_A_ & ps->fade_timer);
|
2019-03-24 04:47:25 +00:00
|
|
|
} else if (fade_running && !ev_is_active(&ps->fade_timer)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_timer_set(&ps->fade_timer, fade_timeout(ps), 0);
|
2019-07-25 01:27:02 +00:00
|
|
|
ev_timer_start(EV_A_ & ps->fade_timer);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-10 22:12:31 +00:00
|
|
|
int64_t after_preprocess_us;
|
|
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
|
|
after_preprocess_us = (now.tv_sec * 1000000LL + now.tv_nsec / 1000);
|
|
|
|
log_trace("paint_preprocess took: %" PRIi64 " us",
|
|
|
|
after_preprocess_us - after_handle_pending_updates_us);
|
|
|
|
|
2023-06-22 23:17:35 +00:00
|
|
|
// If the screen is unredirected, we don't render anything.
|
|
|
|
bool did_render = false;
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->redirected && ps->o.stoppaint_force != ON) {
|
|
|
|
static int paint = 0;
|
2020-03-21 14:59:50 +00:00
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
log_verbose("Render start, frame %d", paint);
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
2023-06-22 23:17:35 +00:00
|
|
|
did_render = paint_all_new(ps, bottom);
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
2022-12-12 10:41:54 +00:00
|
|
|
paint_all(ps, bottom);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2023-07-09 15:39:44 +00:00
|
|
|
log_verbose("Render end");
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-09-28 23:11:32 +00:00
|
|
|
ps->first_frame = false;
|
2019-03-10 12:34:37 +00:00
|
|
|
paint++;
|
2020-10-23 15:41:20 +00:00
|
|
|
if (ps->o.benchmark && paint >= ps->o.benchmark) {
|
2019-03-10 12:34:37 +00:00
|
|
|
exit(0);
|
2020-10-23 15:41:20 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
// With frame pacing, we set backend_busy to true after the end of
|
|
|
|
// vblank. Without frame pacing, we won't be receiving vblank events, so
|
|
|
|
// we set backend_busy to false here, right after we issue the render
|
|
|
|
// commands.
|
|
|
|
// The other case is if we decided there is no change to render, in that
|
|
|
|
// case no render command is issued, so we also set backend_busy to
|
|
|
|
// false.
|
|
|
|
ps->backend_busy = (ps->frame_pacing && did_render);
|
2023-06-22 23:17:35 +00:00
|
|
|
ps->next_render = 0;
|
|
|
|
|
2020-10-23 15:41:20 +00:00
|
|
|
if (!fade_running) {
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->fade_time = 0L;
|
2020-10-23 15:41:20 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-07-09 15:39:44 +00:00
|
|
|
ps->render_queued = false;
|
|
|
|
|
2020-08-30 14:25:58 +00:00
|
|
|
// TODO(yshui) Investigate how big the X critical section needs to be. There are
|
|
|
|
// suggestions that rendering should be in the critical section as well.
|
2019-03-17 15:17:48 +00:00
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
// Queue redraw if animation is running. This should be picked up by next present
|
|
|
|
// event.
|
2023-07-09 15:39:44 +00:00
|
|
|
if (animation) {
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
2023-12-19 23:21:07 +00:00
|
|
|
if (ps->vblank_scheduler) {
|
|
|
|
// Even if we might not want to render during next vblank, we want to keep
|
|
|
|
// `backend_busy` up to date, so when the next render comes, we can
|
|
|
|
// immediately know if we can render.
|
|
|
|
vblank_scheduler_schedule(ps->vblank_scheduler, check_render_finish, ps);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
static void draw_callback(EV_P_ ev_timer *w, int revents) {
|
|
|
|
session_t *ps = session_ptr(w, draw_timer);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2020-08-30 16:44:21 +00:00
|
|
|
draw_callback_impl(EV_A_ ps, revents);
|
2022-12-13 20:56:11 +00:00
|
|
|
ev_timer_stop(EV_A_ w);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
// Immediately start next frame if we are in benchmark mode.
|
|
|
|
if (ps->o.benchmark) {
|
|
|
|
ev_timer_set(w, 0, 0);
|
|
|
|
ev_timer_start(EV_A_ w);
|
2019-07-25 01:27:02 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-07-25 01:27:02 +00:00
|
|
|
static void x_event_callback(EV_P attr_unused, ev_io *w, int revents attr_unused) {
|
2019-03-10 12:34:37 +00:00
|
|
|
session_t *ps = (session_t *)w;
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_generic_event_t *ev = xcb_poll_for_event(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ev) {
|
|
|
|
ev_handle(ps, ev);
|
|
|
|
free(ev);
|
|
|
|
}
|
Improvement #74: Use libevent for main loop
- Use libevent for main loop. I will explain the reasons in #56 later.
The preferred libevent version is 2.x, yet 1.4.x should work as well.
- As a result, compton now should build fine on *BSD. Thanks to
DachiChang for the FreeBSD build issue report.
- Another consequence is we now use microsecond-level timing all the
way. Nanosecond-level code will be dropped soon. Start using long
instead of unsigned long to represent time in milliseconds, as both
can't hold the full epoch time in ms, anyway, and a signed type
requires less care in subtraction. Wrap the epoch time in ms to 15
days.
- Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options.
- Use git revision number for versioning in Makefile, and other small
improvements.
- Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is
turned to bool.
- Add type and format to winprop_t, as preparation for the new condition
format.
- Add w->shadow_force and w->focus_force, to prepare for D-Bus support.
- Rename wid_get_prop() to wid_get_prop_adv() with more options. Add
wrapper function wid_get_prop().
- Add some extra helper functions, for D-Bus support later.
- Make some functions return a bool value to indicate if it's
successful.
- Modify add_win(), use a static const structure to initialize the new
struct _win.
- Add some helper macros, like printf_err(f)(q). Make some errors fatal.
- Rename some types, constants, and functions. Code clean-up.
- Check for time disorder in paint_preprocess() when calculating fading
steps.
- Rename evpoll() to swopti_handle_timeout(), and partially rewrite it.
- Make -h / --help legal.
- Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless
NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang
for reporting.
2013-01-08 00:50:58 +00:00
|
|
|
}
|
|
|
|
|
2018-11-26 23:56:55 +00:00
|
|
|
/**
|
|
|
|
* Turn on the program reset flag.
|
|
|
|
*
|
2024-01-31 03:09:28 +00:00
|
|
|
* This will result in the compositor resetting itself after next paint.
|
2018-11-26 23:56:55 +00:00
|
|
|
*/
|
2019-07-25 01:27:02 +00:00
|
|
|
static void reset_enable(EV_P_ ev_signal *w attr_unused, int revents attr_unused) {
|
2019-10-23 18:27:30 +00:00
|
|
|
log_info("picom is resetting...");
|
2019-07-25 01:27:02 +00:00
|
|
|
ev_break(EV_A_ EVBREAK_ALL);
|
2019-02-11 23:13:39 +00:00
|
|
|
}
|
|
|
|
|
2019-07-25 01:27:02 +00:00
|
|
|
static void exit_enable(EV_P attr_unused, ev_signal *w, int revents attr_unused) {
|
2019-03-10 12:34:37 +00:00
|
|
|
session_t *ps = session_ptr(w, int_signal);
|
2019-10-23 18:27:30 +00:00
|
|
|
log_info("picom is quitting...");
|
|
|
|
quit(ps);
|
2018-11-26 23:56:55 +00:00
|
|
|
}
|
|
|
|
|
2019-11-10 18:58:01 +00:00
|
|
|
static void config_file_change_cb(void *_ps) {
|
|
|
|
auto ps = (struct session *)_ps;
|
|
|
|
reset_enable(ps->loop, NULL, 0);
|
|
|
|
}
|
|
|
|
|
2022-07-17 16:49:35 +00:00
|
|
|
static bool load_shader_source(session_t *ps, const char *path) {
|
|
|
|
if (!path) {
|
|
|
|
// Using the default shader.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
log_info("Loading shader source from %s", path);
|
|
|
|
|
|
|
|
struct shader_info *shader = NULL;
|
|
|
|
HASH_FIND_STR(ps->shaders, path, shader);
|
|
|
|
if (shader) {
|
|
|
|
log_debug("Shader already loaded, reusing");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
shader = ccalloc(1, struct shader_info);
|
|
|
|
shader->key = strdup(path);
|
|
|
|
HASH_ADD_KEYPTR(hh, ps->shaders, shader->key, strlen(shader->key), shader);
|
|
|
|
|
|
|
|
FILE *f = fopen(path, "r");
|
|
|
|
if (!f) {
|
|
|
|
log_error("Failed to open custom shader file: %s", path);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
struct stat statbuf;
|
|
|
|
if (fstat(fileno(f), &statbuf) < 0) {
|
|
|
|
log_error("Failed to access custom shader file: %s", path);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto num_bytes = (size_t)statbuf.st_size;
|
|
|
|
shader->source = ccalloc(num_bytes + 1, char);
|
|
|
|
auto read_bytes = fread(shader->source, sizeof(char), num_bytes, f);
|
|
|
|
if (read_bytes < num_bytes || ferror(f)) {
|
|
|
|
// This is a difficult to hit error case, review thoroughly.
|
|
|
|
log_error("Failed to read custom shader at %s. (read %lu bytes, expected "
|
|
|
|
"%lu bytes)",
|
|
|
|
path, read_bytes, num_bytes);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
err:
|
|
|
|
HASH_DEL(ps->shaders, shader);
|
|
|
|
if (f) {
|
|
|
|
fclose(f);
|
|
|
|
}
|
|
|
|
free(shader->source);
|
|
|
|
free(shader->key);
|
|
|
|
free(shader);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool load_shader_source_for_condition(const c2_lptr_t *cond, void *data) {
|
|
|
|
return load_shader_source(data, c2_list_get_data(cond));
|
|
|
|
}
|
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
/**
|
|
|
|
* Initialize a session.
|
|
|
|
*
|
2024-01-31 03:09:28 +00:00
|
|
|
* @param argc number of command line arguments
|
|
|
|
* @param argv command line arguments
|
2019-02-08 01:27:46 +00:00
|
|
|
* @param dpy the X Display
|
|
|
|
* @param config_file the path to the config file
|
2024-01-31 03:09:28 +00:00
|
|
|
* @param all_xerrors whether we should report all X errors
|
2019-02-08 01:27:46 +00:00
|
|
|
* @param fork whether we will fork after initialization
|
2012-11-19 01:46:07 +00:00
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static session_t *session_init(int argc, char **argv, Display *dpy,
|
|
|
|
const char *config_file, bool all_xerrors, bool fork) {
|
|
|
|
static const session_t s_def = {
|
|
|
|
.backend_data = NULL,
|
|
|
|
.root_height = 0,
|
|
|
|
.root_width = 0,
|
|
|
|
// .root_damage = XCB_NONE,
|
|
|
|
.overlay = XCB_NONE,
|
|
|
|
.root_tile_fill = false,
|
|
|
|
.root_tile_paint = PAINT_INIT,
|
|
|
|
.tgt_picture = XCB_NONE,
|
|
|
|
.tgt_buffer = PAINT_INIT,
|
|
|
|
.reg_win = XCB_NONE,
|
2018-12-21 16:25:28 +00:00
|
|
|
#ifdef CONFIG_OPENGL
|
2019-03-10 12:34:37 +00:00
|
|
|
.glx_prog_win = GLX_PROG_MAIN_INIT,
|
2018-12-21 16:25:28 +00:00
|
|
|
#endif
|
2019-03-10 12:34:37 +00:00
|
|
|
.redirected = false,
|
|
|
|
.alpha_picts = NULL,
|
|
|
|
.fade_time = 0L,
|
|
|
|
.quit = false,
|
|
|
|
|
|
|
|
.expose_rects = NULL,
|
|
|
|
.size_expose = 0,
|
|
|
|
.n_expose = 0,
|
|
|
|
|
|
|
|
.black_picture = XCB_NONE,
|
|
|
|
.cshadow_picture = XCB_NONE,
|
|
|
|
.white_picture = XCB_NONE,
|
2022-08-25 04:21:19 +00:00
|
|
|
.shadow_context = NULL,
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-12-13 20:31:32 +00:00
|
|
|
.last_msc = 0,
|
|
|
|
|
Improvement #74: Use libevent for main loop
- Use libevent for main loop. I will explain the reasons in #56 later.
The preferred libevent version is 2.x, yet 1.4.x should work as well.
- As a result, compton now should build fine on *BSD. Thanks to
DachiChang for the FreeBSD build issue report.
- Another consequence is we now use microsecond-level timing all the
way. Nanosecond-level code will be dropped soon. Start using long
instead of unsigned long to represent time in milliseconds, as both
can't hold the full epoch time in ms, anyway, and a signed type
requires less care in subtraction. Wrap the epoch time in ms to 15
days.
- Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options.
- Use git revision number for versioning in Makefile, and other small
improvements.
- Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is
turned to bool.
- Add type and format to winprop_t, as preparation for the new condition
format.
- Add w->shadow_force and w->focus_force, to prepare for D-Bus support.
- Rename wid_get_prop() to wid_get_prop_adv() with more options. Add
wrapper function wid_get_prop().
- Add some extra helper functions, for D-Bus support later.
- Make some functions return a bool value to indicate if it's
successful.
- Modify add_win(), use a static const structure to initialize the new
struct _win.
- Add some helper macros, like printf_err(f)(q). Make some errors fatal.
- Rename some types, constants, and functions. Code clean-up.
- Check for time disorder in paint_preprocess() when calculating fading
steps.
- Rename evpoll() to swopti_handle_timeout(), and partially rewrite it.
- Make -h / --help legal.
- Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless
NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang
for reporting.
2013-01-08 00:50:58 +00:00
|
|
|
#ifdef CONFIG_VSYNC_DRM
|
2019-03-10 12:34:37 +00:00
|
|
|
.drm_fd = -1,
|
Improvement #74: Use libevent for main loop
- Use libevent for main loop. I will explain the reasons in #56 later.
The preferred libevent version is 2.x, yet 1.4.x should work as well.
- As a result, compton now should build fine on *BSD. Thanks to
DachiChang for the FreeBSD build issue report.
- Another consequence is we now use microsecond-level timing all the
way. Nanosecond-level code will be dropped soon. Start using long
instead of unsigned long to represent time in milliseconds, as both
can't hold the full epoch time in ms, anyway, and a signed type
requires less care in subtraction. Wrap the epoch time in ms to 15
days.
- Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options.
- Use git revision number for versioning in Makefile, and other small
improvements.
- Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is
turned to bool.
- Add type and format to winprop_t, as preparation for the new condition
format.
- Add w->shadow_force and w->focus_force, to prepare for D-Bus support.
- Rename wid_get_prop() to wid_get_prop_adv() with more options. Add
wrapper function wid_get_prop().
- Add some extra helper functions, for D-Bus support later.
- Make some functions return a bool value to indicate if it's
successful.
- Modify add_win(), use a static const structure to initialize the new
struct _win.
- Add some helper macros, like printf_err(f)(q). Make some errors fatal.
- Rename some types, constants, and functions. Code clean-up.
- Check for time disorder in paint_preprocess() when calculating fading
steps.
- Rename evpoll() to swopti_handle_timeout(), and partially rewrite it.
- Make -h / --help legal.
- Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless
NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang
for reporting.
2013-01-08 00:50:58 +00:00
|
|
|
#endif
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
.xfixes_event = 0,
|
|
|
|
.xfixes_error = 0,
|
|
|
|
.damage_event = 0,
|
|
|
|
.damage_error = 0,
|
|
|
|
.render_event = 0,
|
|
|
|
.render_error = 0,
|
|
|
|
.composite_event = 0,
|
|
|
|
.composite_error = 0,
|
|
|
|
.composite_opcode = 0,
|
|
|
|
.shape_exists = false,
|
|
|
|
.shape_event = 0,
|
|
|
|
.shape_error = 0,
|
|
|
|
.randr_exists = 0,
|
|
|
|
.randr_event = 0,
|
|
|
|
.randr_error = 0,
|
|
|
|
.glx_exists = false,
|
|
|
|
.glx_event = 0,
|
|
|
|
.glx_error = 0,
|
|
|
|
.xrfilter_convolution_exists = false,
|
|
|
|
|
Feature #80: D-Bus support
- Add D-Bus support. Currently 7 methods are available: "reset" (same as
SIGUSR1), "list_win" (list the windows compton manages), "win_get"
(get a property of the window), "win_set" (set a property of the
window), "find_win" (find window based on client window / focus),
"opts_get" (get the value of a compton option), and "opts_set" (set
the value of a compton option), together with 4 signals: "win_added",
"win_destroyed", "win_mapped", "win_unmapped".
- D-Bus support depends on libdbus.
- As there are many items and my time is tight, no much tests are done.
Bugs to be expected.
- Create a new header file `common.h` that contains shared content.
- Fix some bugs in timeout handling.
- Update file headers in all source files.
- Re-enable --unredir-if-possible on multi-screen set-ups, as the user
could turn if off manually anyway.
- Check if the window is mapped in `repair_win()`.
- Add ps->track_atom_lst and its handlers, to prepare for the new
condition format.
- Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a
very limited number of targets only. New ones will be added gradually.
- Known issue 2: Accidental drop of D-Bus connection is not handled.
- Known issue 3: Introspection does not reveal all available methods,
because some methods have unpredictable prototypes. Still hesitating
about what to do...
- Known issue 4: Error handling is not finished yet. Compton does not
always reply with the correct error message (but it does print out the
correct error message, usually).
2013-01-19 12:20:27 +00:00
|
|
|
#ifdef CONFIG_DBUS
|
2019-03-10 12:34:37 +00:00
|
|
|
.dbus_data = NULL,
|
Feature #80: D-Bus support
- Add D-Bus support. Currently 7 methods are available: "reset" (same as
SIGUSR1), "list_win" (list the windows compton manages), "win_get"
(get a property of the window), "win_set" (set a property of the
window), "find_win" (find window based on client window / focus),
"opts_get" (get the value of a compton option), and "opts_set" (set
the value of a compton option), together with 4 signals: "win_added",
"win_destroyed", "win_mapped", "win_unmapped".
- D-Bus support depends on libdbus.
- As there are many items and my time is tight, no much tests are done.
Bugs to be expected.
- Create a new header file `common.h` that contains shared content.
- Fix some bugs in timeout handling.
- Update file headers in all source files.
- Re-enable --unredir-if-possible on multi-screen set-ups, as the user
could turn if off manually anyway.
- Check if the window is mapped in `repair_win()`.
- Add ps->track_atom_lst and its handlers, to prepare for the new
condition format.
- Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a
very limited number of targets only. New ones will be added gradually.
- Known issue 2: Accidental drop of D-Bus connection is not handled.
- Known issue 3: Introspection does not reveal all available methods,
because some methods have unpredictable prototypes. Still hesitating
about what to do...
- Known issue 4: Error handling is not finished yet. Compton does not
always reply with the correct error message (but it does print out the
correct error message, usually).
2013-01-19 12:20:27 +00:00
|
|
|
#endif
|
2019-03-10 12:34:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
auto stderr_logger = stderr_logger_new();
|
|
|
|
if (stderr_logger) {
|
|
|
|
// stderr logger might fail to create if we are already
|
|
|
|
// daemonized.
|
|
|
|
log_add_target_tls(stderr_logger);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allocate a session and copy default values into it
|
|
|
|
session_t *ps = cmalloc(session_t);
|
|
|
|
*ps = s_def;
|
|
|
|
ps->loop = EV_DEFAULT;
|
|
|
|
pixman_region32_init(&ps->screen_reg);
|
|
|
|
|
2022-12-13 20:31:32 +00:00
|
|
|
// TODO(yshui) investigate what's the best window size
|
2023-06-10 13:08:49 +00:00
|
|
|
render_statistics_init(&ps->render_stats, 128);
|
2022-12-13 20:31:32 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->o.show_all_xerrors = all_xerrors;
|
|
|
|
|
|
|
|
// Use the same Display across reset, primarily for resource leak checking
|
2023-06-29 04:39:36 +00:00
|
|
|
x_connection_init(&ps->c, dpy);
|
|
|
|
// We store width/height from screen_info instead using them directly because they
|
|
|
|
// can change, see configure_root().
|
|
|
|
ps->root_width = ps->c.screen_info->width_in_pixels;
|
|
|
|
ps->root_height = ps->c.screen_info->height_in_pixels;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
const xcb_query_extension_reply_t *ext_info;
|
|
|
|
|
|
|
|
// Start listening to events on root earlier to catch all possible
|
|
|
|
// root geometry changes
|
|
|
|
auto e = xcb_request_check(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c, xcb_change_window_attributes_checked(
|
|
|
|
ps->c.c, ps->c.screen_info->root, XCB_CW_EVENT_MASK,
|
|
|
|
(const uint32_t[]){XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY |
|
|
|
|
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_STRUCTURE_NOTIFY |
|
|
|
|
XCB_EVENT_MASK_PROPERTY_CHANGE}));
|
2019-03-10 12:34:37 +00:00
|
|
|
if (e) {
|
2020-03-28 00:15:45 +00:00
|
|
|
log_error_x_error(e, "Failed to setup root window event mask");
|
2023-01-09 22:13:08 +00:00
|
|
|
free(e);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_render_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_composite_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_damage_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_shape_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_xfixes_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_randr_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_present_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_sync_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_glx_id);
|
|
|
|
xcb_prefetch_extension_data(ps->c.c, &xcb_dpms_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_render_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!ext_info || !ext_info->present) {
|
|
|
|
log_fatal("No render extension");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ps->render_event = ext_info->first_event;
|
|
|
|
ps->render_error = ext_info->first_error;
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_composite_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!ext_info || !ext_info->present) {
|
|
|
|
log_fatal("No composite extension");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ps->composite_opcode = ext_info->major_opcode;
|
|
|
|
ps->composite_event = ext_info->first_event;
|
|
|
|
ps->composite_error = ext_info->first_error;
|
|
|
|
|
|
|
|
{
|
|
|
|
xcb_composite_query_version_reply_t *reply = xcb_composite_query_version_reply(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c,
|
|
|
|
xcb_composite_query_version(ps->c.c, XCB_COMPOSITE_MAJOR_VERSION,
|
2019-03-10 12:34:37 +00:00
|
|
|
XCB_COMPOSITE_MINOR_VERSION),
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
if (!reply || (reply->major_version == 0 && reply->minor_version < 2)) {
|
|
|
|
log_fatal("Your X server doesn't have Composite >= 0.2 support, "
|
2019-10-23 18:27:30 +00:00
|
|
|
"we cannot proceed.");
|
2019-03-10 12:34:37 +00:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
free(reply);
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_damage_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!ext_info || !ext_info->present) {
|
|
|
|
log_fatal("No damage extension");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ps->damage_event = ext_info->first_event;
|
|
|
|
ps->damage_error = ext_info->first_error;
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_discard_reply(ps->c.c, xcb_damage_query_version(ps->c.c, XCB_DAMAGE_MAJOR_VERSION,
|
|
|
|
XCB_DAMAGE_MINOR_VERSION)
|
|
|
|
.sequence);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_xfixes_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!ext_info || !ext_info->present) {
|
|
|
|
log_fatal("No XFixes extension");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
ps->xfixes_event = ext_info->first_event;
|
|
|
|
ps->xfixes_error = ext_info->first_error;
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_discard_reply(ps->c.c, xcb_xfixes_query_version(ps->c.c, XCB_XFIXES_MAJOR_VERSION,
|
|
|
|
XCB_XFIXES_MINOR_VERSION)
|
|
|
|
.sequence);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2024-03-24 03:22:58 +00:00
|
|
|
ps->damage_ring.x_region = x_new_id(&ps->c);
|
|
|
|
if (!XCB_AWAIT_VOID(xcb_xfixes_create_region, ps->c.c, ps->damage_ring.x_region,
|
|
|
|
0, NULL)) {
|
2020-08-30 08:11:45 +00:00
|
|
|
log_fatal("Failed to create a XFixes region");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_glx_id);
|
2020-03-31 05:07:09 +00:00
|
|
|
if (ext_info && ext_info->present) {
|
|
|
|
ps->glx_exists = true;
|
|
|
|
ps->glx_error = ext_info->first_error;
|
|
|
|
ps->glx_event = ext_info->first_event;
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_dpms_id);
|
2022-12-15 10:53:31 +00:00
|
|
|
ps->dpms_exists = ext_info && ext_info->present;
|
2023-07-05 04:54:44 +00:00
|
|
|
if (!ps->dpms_exists) {
|
2023-12-19 09:53:57 +00:00
|
|
|
log_warn("No DPMS extension");
|
2022-12-15 10:53:31 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Parse configuration file
|
|
|
|
win_option_mask_t winopt_mask[NUM_WINTYPES] = {{0}};
|
|
|
|
bool shadow_enabled = false, fading_enable = false, hasneg = false;
|
|
|
|
char *config_file_to_free = NULL;
|
|
|
|
config_file = config_file_to_free = parse_config(
|
|
|
|
&ps->o, config_file, &shadow_enabled, &fading_enable, &hasneg, winopt_mask);
|
|
|
|
|
|
|
|
if (IS_ERR(config_file_to_free)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse all of the rest command line options
|
2019-11-10 04:59:55 +00:00
|
|
|
if (!get_cfg(&ps->o, argc, argv, shadow_enabled, fading_enable, hasneg, winopt_mask)) {
|
|
|
|
log_fatal("Failed to get configuration, usually mean you have specified "
|
|
|
|
"invalid options.");
|
|
|
|
return NULL;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-07-17 16:49:35 +00:00
|
|
|
if (ps->o.window_shader_fg) {
|
|
|
|
log_debug("Default window shader: \"%s\"", ps->o.window_shader_fg);
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->o.logpath) {
|
|
|
|
auto l = file_logger_new(ps->o.logpath);
|
|
|
|
if (l) {
|
|
|
|
log_info("Switching to log file: %s", ps->o.logpath);
|
|
|
|
if (stderr_logger) {
|
|
|
|
log_remove_target_tls(stderr_logger);
|
|
|
|
stderr_logger = NULL;
|
|
|
|
}
|
|
|
|
log_add_target_tls(l);
|
|
|
|
stderr_logger = NULL;
|
|
|
|
} else {
|
|
|
|
log_error("Failed to setup log file %s, I will keep using stderr",
|
|
|
|
ps->o.logpath);
|
|
|
|
}
|
|
|
|
}
|
2019-07-07 18:55:34 +00:00
|
|
|
|
2019-10-23 18:27:30 +00:00
|
|
|
if (strstr(argv[0], "compton")) {
|
|
|
|
log_warn("This compositor has been renamed to \"picom\", the \"compton\" "
|
|
|
|
"binary will not be installed in the future.");
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->atoms = init_atoms(ps->c.c);
|
2024-02-13 22:47:12 +00:00
|
|
|
ps->c2_state = c2_state_new(ps->atoms);
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Get needed atoms for c2 condition lists
|
2024-02-19 23:30:53 +00:00
|
|
|
options_postprocess_c2_lists(ps->c2_state, &ps->c, &ps->o);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2022-07-17 16:49:35 +00:00
|
|
|
// Load shader source file specified in the shader rules
|
|
|
|
if (c2_list_foreach(ps->o.window_shader_fg_rules, load_shader_source_for_condition, ps)) {
|
|
|
|
log_error("Failed to load shader source file for some of the window "
|
|
|
|
"shader rules");
|
|
|
|
}
|
|
|
|
if (load_shader_source(ps, ps->o.window_shader_fg)) {
|
|
|
|
log_error("Failed to load window shader source file");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log_get_level_tls() <= LOG_LEVEL_DEBUG) {
|
|
|
|
HASH_ITER2(ps->shaders, shader) {
|
|
|
|
log_debug("Shader %s:", shader->key);
|
|
|
|
log_debug("%s", shader->source);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-25 04:21:19 +00:00
|
|
|
if (ps->o.legacy_backends) {
|
|
|
|
ps->shadow_context =
|
|
|
|
(void *)gaussian_kernel_autodetect_deviation(ps->o.shadow_radius);
|
|
|
|
sum_kernel_preprocess((conv *)ps->shadow_context);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
rebuild_shadow_exclude_reg(ps);
|
|
|
|
|
|
|
|
// Query X Shape
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_shape_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ext_info && ext_info->present) {
|
|
|
|
ps->shape_event = ext_info->first_event;
|
|
|
|
ps->shape_error = ext_info->first_error;
|
|
|
|
ps->shape_exists = true;
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_randr_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ext_info && ext_info->present) {
|
|
|
|
ps->randr_exists = true;
|
|
|
|
ps->randr_event = ext_info->first_event;
|
|
|
|
ps->randr_error = ext_info->first_error;
|
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_present_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ext_info && ext_info->present) {
|
|
|
|
auto r = xcb_present_query_version_reply(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c,
|
|
|
|
xcb_present_query_version(ps->c.c, XCB_PRESENT_MAJOR_VERSION,
|
2019-03-10 12:34:37 +00:00
|
|
|
XCB_PRESENT_MINOR_VERSION),
|
|
|
|
NULL);
|
|
|
|
if (r) {
|
|
|
|
ps->present_exists = true;
|
|
|
|
free(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Query X Sync
|
2023-06-29 04:39:36 +00:00
|
|
|
ext_info = xcb_get_extension_data(ps->c.c, &xcb_sync_id);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ext_info && ext_info->present) {
|
|
|
|
ps->xsync_error = ext_info->first_error;
|
|
|
|
ps->xsync_event = ext_info->first_event;
|
|
|
|
// Need X Sync 3.1 for fences
|
|
|
|
auto r = xcb_sync_initialize_reply(
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->c.c,
|
|
|
|
xcb_sync_initialize(ps->c.c, XCB_SYNC_MAJOR_VERSION, XCB_SYNC_MINOR_VERSION),
|
2019-03-10 12:34:37 +00:00
|
|
|
NULL);
|
|
|
|
if (r && (r->major_version > 3 ||
|
|
|
|
(r->major_version == 3 && r->minor_version >= 1))) {
|
|
|
|
ps->xsync_exists = true;
|
|
|
|
free(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ps->sync_fence = XCB_NONE;
|
2020-04-22 19:17:31 +00:00
|
|
|
if (ps->xsync_exists) {
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->sync_fence = x_new_id(&ps->c);
|
|
|
|
e = xcb_request_check(
|
|
|
|
ps->c.c, xcb_sync_create_fence_checked(
|
|
|
|
ps->c.c, ps->c.screen_info->root, ps->sync_fence, 0));
|
2019-03-10 12:34:37 +00:00
|
|
|
if (e) {
|
2020-04-22 19:17:31 +00:00
|
|
|
if (ps->o.xrender_sync_fence) {
|
|
|
|
log_error_x_error(e, "Failed to create a XSync fence. "
|
|
|
|
"xrender-sync-fence will be "
|
|
|
|
"disabled");
|
|
|
|
ps->o.xrender_sync_fence = false;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->sync_fence = XCB_NONE;
|
|
|
|
free(e);
|
|
|
|
}
|
2020-04-22 19:17:31 +00:00
|
|
|
} else if (ps->o.xrender_sync_fence) {
|
|
|
|
log_error("XSync extension not found. No XSync fence sync is "
|
|
|
|
"possible. (xrender-sync-fence can't be enabled)");
|
|
|
|
ps->o.xrender_sync_fence = false;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Query X RandR
|
2023-01-13 11:27:45 +00:00
|
|
|
if (ps->o.crop_shadow_to_monitor && !ps->randr_exists) {
|
|
|
|
log_fatal("No X RandR extension. crop-shadow-to-monitor cannot be "
|
|
|
|
"enabled.");
|
|
|
|
goto err;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
rebuild_screen_reg(ps);
|
|
|
|
|
2020-03-11 19:15:26 +00:00
|
|
|
bool compositor_running = false;
|
|
|
|
if (session_redirection_mode(ps) == XCB_COMPOSITE_REDIRECT_MANUAL) {
|
|
|
|
// We are running in the manual redirection mode, meaning we are running
|
|
|
|
// as a proper compositor. So we need to register us as a compositor, etc.
|
2019-05-06 00:20:38 +00:00
|
|
|
|
2020-03-11 19:15:26 +00:00
|
|
|
// We are also here when --diagnostics is set. We want to be here because
|
|
|
|
// that gives us more diagnostic information.
|
|
|
|
|
|
|
|
// Create registration window
|
|
|
|
int ret = register_cm(ps);
|
|
|
|
if (ret == -1) {
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
compositor_running = ret == 1;
|
|
|
|
if (compositor_running) {
|
|
|
|
// Don't take the overlay when there is another compositor
|
|
|
|
// running, so we don't disrupt it.
|
|
|
|
|
|
|
|
// If we are printing diagnostic, we will continue a bit further
|
|
|
|
// to get more diagnostic information, otherwise we will exit.
|
|
|
|
if (!ps->o.print_diagnostics) {
|
|
|
|
log_fatal("Another composite manager is already running");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
} else {
|
2019-08-04 15:46:09 +00:00
|
|
|
if (!init_overlay(ps)) {
|
|
|
|
goto err;
|
|
|
|
}
|
2020-03-11 19:15:26 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// We are here if we don't really function as a compositor, so we are not
|
|
|
|
// taking over the screen, and we don't need to register as a compositor
|
|
|
|
|
|
|
|
// If we are in debug mode, we need to create a window for rendering if
|
|
|
|
// the backend supports presenting.
|
|
|
|
|
|
|
|
// The old backends doesn't have a automatic redirection mode
|
|
|
|
log_info("The compositor is started in automatic redirection mode.");
|
2022-08-21 05:00:43 +00:00
|
|
|
assert(!ps->o.legacy_backends);
|
2020-03-11 19:15:26 +00:00
|
|
|
|
|
|
|
if (backend_list[ps->o.backend]->present) {
|
|
|
|
// If the backend has `present`, we couldn't be in automatic
|
|
|
|
// redirection mode unless we are in debug mode.
|
|
|
|
assert(ps->o.debug_mode);
|
2019-08-04 15:46:09 +00:00
|
|
|
if (!init_debug_window(ps)) {
|
|
|
|
goto err;
|
|
|
|
}
|
2019-07-07 15:12:17 +00:00
|
|
|
}
|
2019-04-27 13:15:40 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ps->drivers = detect_driver(ps->c.c, ps->backend_data, ps->c.screen_info->root);
|
2020-04-22 19:13:18 +00:00
|
|
|
apply_driver_workarounds(ps, ps->drivers);
|
2019-04-19 23:21:38 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Initialize filters, must be preceded by OpenGL context creation
|
2022-08-21 05:00:43 +00:00
|
|
|
if (ps->o.legacy_backends && !init_render(ps)) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("Failed to initialize the backend");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ps->o.print_diagnostics) {
|
2020-03-11 19:15:26 +00:00
|
|
|
print_diagnostics(ps, config_file, compositor_running);
|
2019-03-10 12:34:37 +00:00
|
|
|
free(config_file_to_free);
|
|
|
|
exit(0);
|
|
|
|
}
|
2019-11-10 18:58:01 +00:00
|
|
|
|
|
|
|
ps->file_watch_handle = file_watch_init(ps->loop);
|
2019-11-10 21:55:38 +00:00
|
|
|
if (ps->file_watch_handle && config_file) {
|
2019-11-10 18:58:01 +00:00
|
|
|
file_watch_add(ps->file_watch_handle, config_file, config_file_change_cb, ps);
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
free(config_file_to_free);
|
|
|
|
|
2022-08-21 05:00:43 +00:00
|
|
|
if (bkend_use_glx(ps) && ps->o.legacy_backends) {
|
2019-03-27 14:51:20 +00:00
|
|
|
auto gl_logger = gl_string_marker_logger_new();
|
|
|
|
if (gl_logger) {
|
2019-03-10 12:34:37 +00:00
|
|
|
log_info("Enabling gl string marker");
|
2019-03-27 14:51:20 +00:00
|
|
|
log_add_target_tls(gl_logger);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->o.monitor_repaint && !backend_list[ps->o.backend]->fill) {
|
|
|
|
log_warn("--monitor-repaint is not supported by the backend, "
|
|
|
|
"disabling");
|
|
|
|
ps->o.monitor_repaint = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Monitor screen changes if vsync_sw is enabled and we are using
|
2023-01-13 11:27:45 +00:00
|
|
|
// an auto-detected refresh rate, or when X RandR features are enabled
|
|
|
|
if (ps->randr_exists && ps->o.crop_shadow_to_monitor) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_randr_select_input(ps->c.c, ps->c.screen_info->root,
|
|
|
|
XCB_RANDR_NOTIFY_MASK_SCREEN_CHANGE);
|
|
|
|
x_update_monitors(&ps->c, &ps->monitors);
|
2022-01-24 17:57:46 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
xcb_render_create_picture_value_list_t pa = {
|
|
|
|
.subwindowmode = IncludeInferiors,
|
|
|
|
};
|
|
|
|
|
|
|
|
ps->root_picture = x_create_picture_with_visual_and_pixmap(
|
2023-06-29 04:39:36 +00:00
|
|
|
&ps->c, ps->c.screen_info->root_visual, ps->c.screen_info->root,
|
|
|
|
XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->overlay != XCB_NONE) {
|
|
|
|
ps->tgt_picture = x_create_picture_with_visual_and_pixmap(
|
2023-06-29 04:39:36 +00:00
|
|
|
&ps->c, ps->c.screen_info->root_visual, ps->overlay,
|
|
|
|
XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
|
|
|
} else {
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->tgt_picture = ps->root_picture;
|
2023-06-29 04:39:36 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
ev_io_init(&ps->xiow, x_event_callback, ConnectionNumber(ps->c.dpy), EV_READ);
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_io_start(ps->loop, &ps->xiow);
|
|
|
|
ev_init(&ps->unredir_timer, tmout_unredir_callback);
|
2022-12-13 20:56:11 +00:00
|
|
|
ev_init(&ps->draw_timer, draw_callback);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
ev_init(&ps->fade_timer, fade_timer_callback);
|
|
|
|
|
|
|
|
// Set up SIGUSR1 signal handler to reset program
|
|
|
|
ev_signal_init(&ps->usr1_signal, reset_enable, SIGUSR1);
|
|
|
|
ev_signal_init(&ps->int_signal, exit_enable, SIGINT);
|
|
|
|
ev_signal_start(ps->loop, &ps->usr1_signal);
|
|
|
|
ev_signal_start(ps->loop, &ps->int_signal);
|
|
|
|
|
|
|
|
// xcb can read multiple events from the socket when a request with reply is
|
|
|
|
// made.
|
|
|
|
//
|
|
|
|
// Use an ev_prepare to make sure we cannot accidentally forget to handle them
|
|
|
|
// before we go to sleep.
|
|
|
|
//
|
|
|
|
// If we don't drain the queue before goes to sleep (i.e. blocking on socket
|
|
|
|
// input), we will be sleeping with events available in queue. Which might
|
|
|
|
// cause us to block indefinitely because arrival of new events could be
|
|
|
|
// dependent on processing of existing events (e.g. if we don't process damage
|
|
|
|
// event and do damage subtract, new damage event won't be generated).
|
|
|
|
//
|
|
|
|
// So we make use of a ev_prepare handle, which is called right before libev
|
|
|
|
// goes into sleep, to handle all the queued X events.
|
|
|
|
ev_prepare_init(&ps->event_check, handle_queued_x_events);
|
|
|
|
// Make sure nothing can cause xcb to read from the X socket after events are
|
|
|
|
// handled and before we going to sleep.
|
|
|
|
ev_set_priority(&ps->event_check, EV_MINPRI);
|
|
|
|
ev_prepare_start(ps->loop, &ps->event_check);
|
|
|
|
|
|
|
|
// Initialize DBus. We need to do this early, because add_win might call dbus
|
|
|
|
// functions
|
|
|
|
if (ps->o.dbus) {
|
2018-12-20 22:31:34 +00:00
|
|
|
#ifdef CONFIG_DBUS
|
2024-03-23 19:27:42 +00:00
|
|
|
ps->dbus_data = cdbus_init(ps, DisplayString(ps->c.dpy));
|
2019-03-10 12:34:37 +00:00
|
|
|
if (!ps->dbus_data) {
|
|
|
|
ps->o.dbus = false;
|
|
|
|
}
|
2018-12-20 22:31:34 +00:00
|
|
|
#else
|
2019-03-10 12:34:37 +00:00
|
|
|
log_fatal("DBus support not compiled in!");
|
|
|
|
exit(1);
|
2018-12-20 22:31:34 +00:00
|
|
|
#endif
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
e = xcb_request_check(ps->c.c, xcb_grab_server_checked(ps->c.c));
|
2019-08-09 22:34:37 +00:00
|
|
|
if (e) {
|
2020-03-28 00:18:07 +00:00
|
|
|
log_fatal_x_error(e, "Failed to grab X server");
|
|
|
|
free(e);
|
2019-08-09 22:34:37 +00:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2019-08-07 23:46:56 +00:00
|
|
|
ps->server_grabbed = true;
|
|
|
|
|
2019-08-09 22:34:37 +00:00
|
|
|
// We are going to pull latest information from X server now, events sent by X
|
2024-01-31 03:09:28 +00:00
|
|
|
// earlier is irrelevant at this point.
|
2019-08-09 22:34:37 +00:00
|
|
|
// A better solution is probably grabbing the server from the very start. But I
|
|
|
|
// think there still could be race condition that mandates discarding the events.
|
2023-06-29 04:39:36 +00:00
|
|
|
x_discard_events(&ps->c);
|
2019-08-09 22:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_query_tree_reply_t *query_tree_reply = xcb_query_tree_reply(
|
|
|
|
ps->c.c, xcb_query_tree(ps->c.c, ps->c.screen_info->root), NULL);
|
2019-08-10 00:23:11 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
e = xcb_request_check(ps->c.c, xcb_ungrab_server_checked(ps->c.c));
|
2019-08-10 00:23:11 +00:00
|
|
|
if (e) {
|
2020-03-28 00:18:07 +00:00
|
|
|
log_fatal_x_error(e, "Failed to ungrab server");
|
2019-08-10 00:23:11 +00:00
|
|
|
free(e);
|
2020-03-28 00:18:07 +00:00
|
|
|
goto err;
|
2019-08-10 00:23:11 +00:00
|
|
|
}
|
|
|
|
|
2019-08-07 23:46:56 +00:00
|
|
|
ps->server_grabbed = false;
|
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
ps->wm = wm_new();
|
2019-08-10 00:23:11 +00:00
|
|
|
if (query_tree_reply) {
|
2019-03-10 12:34:37 +00:00
|
|
|
xcb_window_t *children;
|
|
|
|
int nchildren;
|
|
|
|
|
2019-08-10 00:23:11 +00:00
|
|
|
children = xcb_query_tree_children(query_tree_reply);
|
|
|
|
nchildren = xcb_query_tree_children_length(query_tree_reply);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < nchildren; i++) {
|
2024-03-26 17:54:34 +00:00
|
|
|
wm_stack_add_above(ps->wm, children[i], i ? children[i - 1] : XCB_NONE);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
2019-08-10 00:23:11 +00:00
|
|
|
free(query_tree_reply);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-09-19 16:23:48 +00:00
|
|
|
log_debug("Initial stack:");
|
2024-03-26 17:54:34 +00:00
|
|
|
list_foreach(struct win, w, wm_stack_end(ps->wm), stack_neighbour) {
|
2019-09-19 16:23:48 +00:00
|
|
|
log_debug("%#010x", w->id);
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
|
2019-05-24 23:06:41 +00:00
|
|
|
ps->pending_updates = true;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
write_pid(ps);
|
|
|
|
|
|
|
|
if (fork && stderr_logger) {
|
|
|
|
// Remove the stderr logger if we will fork
|
|
|
|
log_remove_target_tls(stderr_logger);
|
|
|
|
}
|
|
|
|
return ps;
|
2019-04-19 21:35:52 +00:00
|
|
|
err:
|
|
|
|
free(ps);
|
|
|
|
return NULL;
|
2012-11-19 01:46:07 +00:00
|
|
|
}
|
2023-06-26 12:25:53 +00:00
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
/**
|
|
|
|
* Destroy a session.
|
|
|
|
*
|
|
|
|
* Does not close the X connection or free the <code>session_t</code>
|
|
|
|
* structure, though.
|
|
|
|
*
|
|
|
|
* @param ps session to destroy
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static void session_destroy(session_t *ps) {
|
2019-03-24 04:47:25 +00:00
|
|
|
if (ps->redirected) {
|
2019-09-28 22:43:36 +00:00
|
|
|
unredirect(ps);
|
2019-03-24 04:47:25 +00:00
|
|
|
}
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2019-11-10 18:58:01 +00:00
|
|
|
file_watch_destroy(ps->loop, ps->file_watch_handle);
|
|
|
|
ps->file_watch_handle = NULL;
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Stop listening to events on root window
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_change_window_attributes(ps->c.c, ps->c.screen_info->root, XCB_CW_EVENT_MASK,
|
2019-03-10 12:34:37 +00:00
|
|
|
(const uint32_t[]){0});
|
2011-11-04 06:33:23 +00:00
|
|
|
|
Feature #80: D-Bus support
- Add D-Bus support. Currently 7 methods are available: "reset" (same as
SIGUSR1), "list_win" (list the windows compton manages), "win_get"
(get a property of the window), "win_set" (set a property of the
window), "find_win" (find window based on client window / focus),
"opts_get" (get the value of a compton option), and "opts_set" (set
the value of a compton option), together with 4 signals: "win_added",
"win_destroyed", "win_mapped", "win_unmapped".
- D-Bus support depends on libdbus.
- As there are many items and my time is tight, no much tests are done.
Bugs to be expected.
- Create a new header file `common.h` that contains shared content.
- Fix some bugs in timeout handling.
- Update file headers in all source files.
- Re-enable --unredir-if-possible on multi-screen set-ups, as the user
could turn if off manually anyway.
- Check if the window is mapped in `repair_win()`.
- Add ps->track_atom_lst and its handlers, to prepare for the new
condition format.
- Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a
very limited number of targets only. New ones will be added gradually.
- Known issue 2: Accidental drop of D-Bus connection is not handled.
- Known issue 3: Introspection does not reveal all available methods,
because some methods have unpredictable prototypes. Still hesitating
about what to do...
- Known issue 4: Error handling is not finished yet. Compton does not
always reply with the correct error message (but it does print out the
correct error message, usually).
2013-01-19 12:20:27 +00:00
|
|
|
#ifdef CONFIG_DBUS
|
2019-03-10 12:34:37 +00:00
|
|
|
// Kill DBus connection
|
|
|
|
if (ps->o.dbus) {
|
|
|
|
assert(ps->dbus_data);
|
2024-03-23 19:27:42 +00:00
|
|
|
cdbus_destroy(ps->dbus_data);
|
|
|
|
ps->dbus_data = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
Feature #80: D-Bus support
- Add D-Bus support. Currently 7 methods are available: "reset" (same as
SIGUSR1), "list_win" (list the windows compton manages), "win_get"
(get a property of the window), "win_set" (set a property of the
window), "find_win" (find window based on client window / focus),
"opts_get" (get the value of a compton option), and "opts_set" (set
the value of a compton option), together with 4 signals: "win_added",
"win_destroyed", "win_mapped", "win_unmapped".
- D-Bus support depends on libdbus.
- As there are many items and my time is tight, no much tests are done.
Bugs to be expected.
- Create a new header file `common.h` that contains shared content.
- Fix some bugs in timeout handling.
- Update file headers in all source files.
- Re-enable --unredir-if-possible on multi-screen set-ups, as the user
could turn if off manually anyway.
- Check if the window is mapped in `repair_win()`.
- Add ps->track_atom_lst and its handlers, to prepare for the new
condition format.
- Known issue 1: "win_get", "win_set", "opts_get", "opts_set" support a
very limited number of targets only. New ones will be added gradually.
- Known issue 2: Accidental drop of D-Bus connection is not handled.
- Known issue 3: Introspection does not reveal all available methods,
because some methods have unpredictable prototypes. Still hesitating
about what to do...
- Known issue 4: Error handling is not finished yet. Compton does not
always reply with the correct error message (but it does print out the
correct error message, usually).
2013-01-19 12:20:27 +00:00
|
|
|
#endif
|
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
win_stack_foreach_managed_safe(w, wm_stack_end(ps->wm)) {
|
|
|
|
free_win_res(ps, w);
|
2024-03-25 18:05:46 +00:00
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Free blacklists
|
2024-02-20 00:18:39 +00:00
|
|
|
options_destroy(&ps->o);
|
2024-02-13 22:47:12 +00:00
|
|
|
c2_state_free(ps->c2_state);
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
// Free tgt_{buffer,picture} and root_picture
|
2022-12-14 14:23:55 +00:00
|
|
|
if (ps->tgt_buffer.pict == ps->tgt_picture) {
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->tgt_buffer.pict = XCB_NONE;
|
2022-12-14 14:23:55 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
if (ps->tgt_picture != ps->root_picture) {
|
|
|
|
x_free_picture(&ps->c, ps->tgt_picture);
|
2022-12-14 14:23:55 +00:00
|
|
|
}
|
2023-06-29 04:39:36 +00:00
|
|
|
x_free_picture(&ps->c, ps->root_picture);
|
|
|
|
ps->tgt_picture = ps->root_picture = XCB_NONE;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
free_paint(ps, &ps->tgt_buffer);
|
|
|
|
|
|
|
|
pixman_region32_fini(&ps->screen_reg);
|
|
|
|
free(ps->expose_rects);
|
|
|
|
|
2023-06-29 04:39:36 +00:00
|
|
|
x_free_monitor_info(&ps->monitors);
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2023-06-10 13:08:49 +00:00
|
|
|
render_statistics_destroy(&ps->render_stats);
|
2022-12-13 20:31:32 +00:00
|
|
|
|
2022-07-17 16:49:35 +00:00
|
|
|
// Release custom window shaders
|
|
|
|
free(ps->o.window_shader_fg);
|
|
|
|
struct shader_info *shader, *tmp;
|
|
|
|
HASH_ITER(hh, ps->shaders, shader, tmp) {
|
|
|
|
HASH_DEL(ps->shaders, shader);
|
|
|
|
assert(shader->backend_shader == NULL);
|
|
|
|
free(shader->source);
|
|
|
|
free(shader->key);
|
|
|
|
free(shader);
|
|
|
|
}
|
|
|
|
|
Improvement #74: Use libevent for main loop
- Use libevent for main loop. I will explain the reasons in #56 later.
The preferred libevent version is 2.x, yet 1.4.x should work as well.
- As a result, compton now should build fine on *BSD. Thanks to
DachiChang for the FreeBSD build issue report.
- Another consequence is we now use microsecond-level timing all the
way. Nanosecond-level code will be dropped soon. Start using long
instead of unsigned long to represent time in milliseconds, as both
can't hold the full epoch time in ms, anyway, and a signed type
requires less care in subtraction. Wrap the epoch time in ms to 15
days.
- Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options.
- Use git revision number for versioning in Makefile, and other small
improvements.
- Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is
turned to bool.
- Add type and format to winprop_t, as preparation for the new condition
format.
- Add w->shadow_force and w->focus_force, to prepare for D-Bus support.
- Rename wid_get_prop() to wid_get_prop_adv() with more options. Add
wrapper function wid_get_prop().
- Add some extra helper functions, for D-Bus support later.
- Make some functions return a bool value to indicate if it's
successful.
- Modify add_win(), use a static const structure to initialize the new
struct _win.
- Add some helper macros, like printf_err(f)(q). Make some errors fatal.
- Rename some types, constants, and functions. Code clean-up.
- Check for time disorder in paint_preprocess() when calculating fading
steps.
- Rename evpoll() to swopti_handle_timeout(), and partially rewrite it.
- Make -h / --help legal.
- Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless
NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang
for reporting.
2013-01-08 00:50:58 +00:00
|
|
|
#ifdef CONFIG_VSYNC_DRM
|
2019-03-10 12:34:37 +00:00
|
|
|
// Close file opened for DRM VSync
|
|
|
|
if (ps->drm_fd >= 0) {
|
|
|
|
close(ps->drm_fd);
|
|
|
|
ps->drm_fd = -1;
|
|
|
|
}
|
Improvement #74: Use libevent for main loop
- Use libevent for main loop. I will explain the reasons in #56 later.
The preferred libevent version is 2.x, yet 1.4.x should work as well.
- As a result, compton now should build fine on *BSD. Thanks to
DachiChang for the FreeBSD build issue report.
- Another consequence is we now use microsecond-level timing all the
way. Nanosecond-level code will be dropped soon. Start using long
instead of unsigned long to represent time in milliseconds, as both
can't hold the full epoch time in ms, anyway, and a signed type
requires less care in subtraction. Wrap the epoch time in ms to 15
days.
- Fix broken NO_VSYNC_DRM and NO_VSYNC_OPENGL compile-time options.
- Use git revision number for versioning in Makefile, and other small
improvements.
- Reorganize struct _win. Drop unused w->damaged_sequence. w->damaged is
turned to bool.
- Add type and format to winprop_t, as preparation for the new condition
format.
- Add w->shadow_force and w->focus_force, to prepare for D-Bus support.
- Rename wid_get_prop() to wid_get_prop_adv() with more options. Add
wrapper function wid_get_prop().
- Add some extra helper functions, for D-Bus support later.
- Make some functions return a bool value to indicate if it's
successful.
- Modify add_win(), use a static const structure to initialize the new
struct _win.
- Add some helper macros, like printf_err(f)(q). Make some errors fatal.
- Rename some types, constants, and functions. Code clean-up.
- Check for time disorder in paint_preprocess() when calculating fading
steps.
- Rename evpoll() to swopti_handle_timeout(), and partially rewrite it.
- Make -h / --help legal.
- Known issue: compton segfaults on FreeBSD with nvidia-drivers, unless
NO_VSYNC_OPENGL is used. Will look into it later. Thamls to DachiChang
for reporting.
2013-01-08 00:50:58 +00:00
|
|
|
#endif
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Release overlay window
|
|
|
|
if (ps->overlay) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_composite_release_overlay_window(ps->c.c, ps->overlay);
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->overlay = XCB_NONE;
|
|
|
|
}
|
|
|
|
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->sync_fence != XCB_NONE) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_sync_destroy_fence(ps->c.c, ps->sync_fence);
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->sync_fence = XCB_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Free reg_win
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->reg_win != XCB_NONE) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_destroy_window(ps->c.c, ps->reg_win);
|
2019-03-10 12:34:37 +00:00
|
|
|
ps->reg_win = XCB_NONE;
|
|
|
|
}
|
|
|
|
|
2020-08-30 13:10:26 +00:00
|
|
|
if (ps->debug_window != XCB_NONE) {
|
2023-06-29 04:39:36 +00:00
|
|
|
xcb_destroy_window(ps->c.c, ps->debug_window);
|
2019-07-25 01:36:35 +00:00
|
|
|
ps->debug_window = XCB_NONE;
|
|
|
|
}
|
|
|
|
|
2024-03-24 03:22:58 +00:00
|
|
|
if (ps->damage_ring.x_region != XCB_NONE) {
|
|
|
|
xcb_xfixes_destroy_region(ps->c.c, ps->damage_ring.x_region);
|
|
|
|
ps->damage_ring.x_region = XCB_NONE;
|
2020-08-30 08:11:45 +00:00
|
|
|
}
|
|
|
|
|
2022-08-21 05:00:43 +00:00
|
|
|
if (!ps->o.legacy_backends) {
|
2019-09-28 22:43:36 +00:00
|
|
|
// backend is deinitialized in unredirect()
|
2019-03-10 12:34:37 +00:00
|
|
|
assert(ps->backend_data == NULL);
|
|
|
|
} else {
|
|
|
|
deinit_render(ps);
|
|
|
|
}
|
|
|
|
|
2019-06-06 06:40:32 +00:00
|
|
|
#if CONFIG_OPENGL
|
2019-06-06 06:33:07 +00:00
|
|
|
if (glx_has_context(ps)) {
|
|
|
|
// GLX context created, but not for rendering
|
|
|
|
glx_destroy(ps);
|
|
|
|
}
|
2019-06-06 06:40:32 +00:00
|
|
|
#endif
|
2019-06-06 06:33:07 +00:00
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Flush all events
|
2024-02-14 17:30:39 +00:00
|
|
|
xcb_aux_sync(ps->c.c);
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_io_stop(ps->loop, &ps->xiow);
|
2022-08-25 04:21:19 +00:00
|
|
|
if (ps->o.legacy_backends) {
|
|
|
|
free_conv((conv *)ps->shadow_context);
|
|
|
|
}
|
2019-05-05 23:34:08 +00:00
|
|
|
destroy_atoms(ps->atoms);
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2014-08-03 11:40:40 +00:00
|
|
|
#ifdef DEBUG_XRC
|
2019-03-10 12:34:37 +00:00
|
|
|
// Report about resource leakage
|
|
|
|
xrc_report_xid();
|
2014-08-03 11:40:40 +00:00
|
|
|
#endif
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
// Stop libev event handlers
|
|
|
|
ev_timer_stop(ps->loop, &ps->unredir_timer);
|
|
|
|
ev_timer_stop(ps->loop, &ps->fade_timer);
|
2022-12-13 20:56:11 +00:00
|
|
|
ev_timer_stop(ps->loop, &ps->draw_timer);
|
2019-03-10 12:34:37 +00:00
|
|
|
ev_prepare_stop(ps->loop, &ps->event_check);
|
|
|
|
ev_signal_stop(ps->loop, &ps->usr1_signal);
|
|
|
|
ev_signal_stop(ps->loop, &ps->int_signal);
|
2023-06-29 04:39:36 +00:00
|
|
|
|
2024-03-26 17:54:34 +00:00
|
|
|
wm_free(ps->wm, &ps->c);
|
2023-06-29 04:39:36 +00:00
|
|
|
free_x_connection(&ps->c);
|
2012-11-19 01:46:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Do the actual work.
|
|
|
|
*
|
|
|
|
* @param ps current session
|
|
|
|
*/
|
2019-03-10 12:34:37 +00:00
|
|
|
static void session_run(session_t *ps) {
|
2024-03-03 01:24:26 +00:00
|
|
|
if (ps->o.use_realtime_scheduling) {
|
|
|
|
set_rr_scheduling();
|
|
|
|
}
|
|
|
|
|
2022-12-13 20:56:11 +00:00
|
|
|
// In benchmark mode, we want draw_timer handler to always be active
|
2019-03-10 12:34:37 +00:00
|
|
|
if (ps->o.benchmark) {
|
2022-12-13 20:56:11 +00:00
|
|
|
ev_timer_set(&ps->draw_timer, 0, 0);
|
|
|
|
ev_timer_start(ps->loop, &ps->draw_timer);
|
2019-03-10 12:34:37 +00:00
|
|
|
} else {
|
|
|
|
// Let's draw our first frame!
|
|
|
|
queue_redraw(ps);
|
|
|
|
}
|
|
|
|
ev_run(ps->loop, 0);
|
2003-11-09 07:08:23 +00:00
|
|
|
}
|
2012-11-19 01:46:07 +00:00
|
|
|
|
2024-02-18 18:25:21 +00:00
|
|
|
#ifdef CONFIG_FUZZER
|
|
|
|
#define PICOM_MAIN(...) no_main(__VA_ARGS__)
|
|
|
|
#else
|
|
|
|
#define PICOM_MAIN(...) main(__VA_ARGS__)
|
|
|
|
#endif
|
|
|
|
|
2012-11-19 01:46:07 +00:00
|
|
|
/**
|
|
|
|
* The function that everybody knows.
|
|
|
|
*/
|
2024-02-18 18:25:21 +00:00
|
|
|
int PICOM_MAIN(int argc, char **argv) {
|
2019-03-10 12:34:37 +00:00
|
|
|
// Set locale so window names with special characters are interpreted
|
|
|
|
// correctly
|
|
|
|
setlocale(LC_ALL, "");
|
2019-11-10 19:12:16 +00:00
|
|
|
|
|
|
|
// Initialize logging system for early logging
|
2019-03-21 22:37:35 +00:00
|
|
|
log_init_tls();
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-11-10 19:12:16 +00:00
|
|
|
{
|
|
|
|
auto stderr_logger = stderr_logger_new();
|
|
|
|
if (stderr_logger) {
|
|
|
|
log_add_target_tls(stderr_logger);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
int exit_code;
|
|
|
|
char *config_file = NULL;
|
|
|
|
bool all_xerrors = false, need_fork = false;
|
|
|
|
if (get_early_config(argc, argv, &config_file, &all_xerrors, &need_fork, &exit_code)) {
|
|
|
|
return exit_code;
|
|
|
|
}
|
|
|
|
|
2024-02-20 00:34:26 +00:00
|
|
|
char *exe_name = basename(argv[0]);
|
|
|
|
if (strcmp(exe_name, "picom-inspect") == 0) {
|
|
|
|
return inspect_main(argc, argv, config_file);
|
|
|
|
}
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
int pfds[2];
|
|
|
|
if (need_fork) {
|
|
|
|
if (pipe2(pfds, O_CLOEXEC)) {
|
|
|
|
perror("pipe2");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
auto pid = fork();
|
|
|
|
if (pid < 0) {
|
|
|
|
perror("fork");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
if (pid > 0) {
|
|
|
|
// We are the parent
|
|
|
|
close(pfds[1]);
|
|
|
|
// We wait for the child to tell us it has finished initialization
|
|
|
|
// by sending us something via the pipe.
|
|
|
|
int tmp;
|
|
|
|
if (read(pfds[0], &tmp, sizeof tmp) <= 0) {
|
|
|
|
// Failed to read, the child has most likely died
|
|
|
|
// We can probably waitpid() here.
|
|
|
|
return 1;
|
|
|
|
}
|
2023-06-29 04:39:36 +00:00
|
|
|
// We are done
|
|
|
|
return 0;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
// We are the child
|
|
|
|
close(pfds[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Main loop
|
|
|
|
bool quit = false;
|
2019-11-10 19:10:32 +00:00
|
|
|
int ret_code = 0;
|
2020-09-16 18:25:10 +00:00
|
|
|
char *pid_file = NULL;
|
2019-03-10 12:34:37 +00:00
|
|
|
|
|
|
|
do {
|
2019-07-25 20:15:47 +00:00
|
|
|
Display *dpy = XOpenDisplay(NULL);
|
|
|
|
if (!dpy) {
|
2019-11-10 19:12:16 +00:00
|
|
|
log_fatal("Can't open display.");
|
2019-11-10 19:10:32 +00:00
|
|
|
ret_code = 1;
|
|
|
|
break;
|
2019-07-25 20:15:47 +00:00
|
|
|
}
|
|
|
|
XSetEventQueueOwner(dpy, XCBOwnsEventQueue);
|
2019-11-10 19:10:32 +00:00
|
|
|
|
|
|
|
// Reinit logging system so we don't get leftovers from previous sessions
|
|
|
|
// or early logging.
|
|
|
|
log_deinit_tls();
|
|
|
|
log_init_tls();
|
|
|
|
|
2019-03-10 12:34:37 +00:00
|
|
|
ps_g = session_init(argc, argv, dpy, config_file, all_xerrors, need_fork);
|
|
|
|
if (!ps_g) {
|
2019-10-23 18:27:30 +00:00
|
|
|
log_fatal("Failed to create new session.");
|
2019-11-10 19:10:32 +00:00
|
|
|
ret_code = 1;
|
|
|
|
break;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
if (need_fork) {
|
|
|
|
// Finishing up daemonization
|
|
|
|
// Close files
|
|
|
|
if (fclose(stdout) || fclose(stderr) || fclose(stdin)) {
|
|
|
|
log_fatal("Failed to close standard input/output");
|
2019-11-10 19:10:32 +00:00
|
|
|
ret_code = 1;
|
|
|
|
break;
|
2019-03-10 12:34:37 +00:00
|
|
|
}
|
|
|
|
// Make us the session and process group leader so we don't get
|
|
|
|
// killed when our parent die.
|
|
|
|
setsid();
|
|
|
|
// Notify the parent that we are done. This might cause the parent
|
|
|
|
// to quit, so only do this after setsid()
|
|
|
|
int tmp = 1;
|
2022-12-14 13:47:38 +00:00
|
|
|
if (write(pfds[1], &tmp, sizeof tmp) != sizeof tmp) {
|
|
|
|
log_fatal("Failed to notify parent process");
|
|
|
|
ret_code = 1;
|
|
|
|
break;
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
close(pfds[1]);
|
|
|
|
// We only do this once
|
|
|
|
need_fork = false;
|
|
|
|
}
|
|
|
|
session_run(ps_g);
|
|
|
|
quit = ps_g->quit;
|
2020-09-16 18:25:10 +00:00
|
|
|
if (quit && ps_g->o.write_pid_path) {
|
|
|
|
pid_file = strdup(ps_g->o.write_pid_path);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
session_destroy(ps_g);
|
|
|
|
free(ps_g);
|
|
|
|
ps_g = NULL;
|
2019-07-25 20:15:47 +00:00
|
|
|
if (dpy) {
|
|
|
|
XCloseDisplay(dpy);
|
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
} while (!quit);
|
|
|
|
|
|
|
|
free(config_file);
|
2020-09-16 18:25:10 +00:00
|
|
|
if (pid_file) {
|
|
|
|
log_trace("remove pid file %s", pid_file);
|
|
|
|
unlink(pid_file);
|
2020-10-22 13:44:10 +00:00
|
|
|
free(pid_file);
|
2020-09-16 18:25:10 +00:00
|
|
|
}
|
2019-03-10 12:34:37 +00:00
|
|
|
|
2019-07-25 01:34:50 +00:00
|
|
|
log_deinit_tls();
|
|
|
|
|
2019-11-10 19:10:32 +00:00
|
|
|
return ret_code;
|
2012-11-19 01:46:07 +00:00
|
|
|
}
|