Skip to content

Commit a65af25

Browse files
borisi1claude
andauthored
fix: autorotate-iio: fix D-Bus events not processed when display is idle (#337)
* fix: autorotate-iio: fix D-Bus events not processed when display is idle The plugin used an OUTPUT_EFFECT_PRE hook (on_frame) to pump the GLib main context. This hook only fires while the compositor is actively rendering frames for that output. When the display is idle — no damaged windows, no animations — no frames are rendered, so GLib's main context never runs and property-change signals from iio-sensor-proxy queue up unprocessed. Rotation stops working until something triggers a redraw (e.g. an active terminal window causes continuous repaints). Fix: replace on_frame with a wl_event_loop_add_timer(50ms) wired into the Wayland event loop via wl_display_get_event_loop(). This pumps the GLib main context at a steady rate regardless of display activity. Additional fixes: - Guard against a null Glib::Variant from get_cached_property() when the D-Bus proxy cache is not yet populated; calling .get() on an uninitialized Variant crashes the compositor. - Call update_orientation() immediately after ClaimAccelerometer so the initial device orientation is applied without waiting for a change event. - Add an asymmetric debounce state machine to handle noisy sensors: 500ms to commit a rotation, 1200ms to return to normal. The longer normal timeout prevents the screen from flipping back during the brief "normal" readings that occur mid-rotation on some hardware. The rotation timer is not reset on subsequent non-normal readings so the delay is measured from the first reading, not the last. * fix code style to pass uncrustify CI check Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8d73a10 commit a65af25

1 file changed

Lines changed: 83 additions & 7 deletions

File tree

src/autorotate-iio.cpp

Lines changed: 83 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ extern "C"
2222
{
2323
#include <wlr/types/wlr_cursor.h>
2424
#include <wlr/types/wlr_seat.h>
25+
#include <wayland-server-core.h>
2526
}
2627

2728
using namespace Gio;
@@ -105,6 +106,12 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
105106
/* Transform coming from the iio-sensors, -1 means not set */
106107
int32_t sensor_transform = -1;
107108

109+
/* Debounce: pending transform waiting to be confirmed stable */
110+
int32_t pending_transform = -1;
111+
guint debounce_timer_id = 0;
112+
static constexpr guint DEBOUNCE_ROTATE_MS = 500;
113+
static constexpr guint DEBOUNCE_NORMAL_MS = 1200;
114+
108115
bool on_rotate_binding(int32_t target_rotation)
109116
{
110117
if (!output->can_activate_plugin(&grab_interface))
@@ -154,10 +161,18 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
154161
return true;
155162
}
156163

157-
wf::effect_hook_t on_frame = [=] ()
164+
/* Wayland event loop timer — fires every 50ms to pump the GLib main context
165+
* even when the display is idle and on_frame would not run. */
166+
wl_event_source *glib_timer = nullptr;
167+
168+
static int glib_timer_cb(void *data)
158169
{
170+
auto *self = static_cast<WayfireAutorotateIIO*>(data);
159171
Glib::MainContext::get_default()->iteration(false);
160-
};
172+
wl_event_source_timer_update(self->glib_timer, 50);
173+
return 0;
174+
}
175+
161176
Glib::RefPtr<Glib::MainLoop> loop;
162177

163178
public:
@@ -185,7 +200,10 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
185200
Gio::init();
186201

187202
loop = Glib::MainLoop::create(true);
188-
output->render->add_effect(&on_frame, wf::OUTPUT_EFFECT_PRE);
203+
204+
auto *evloop = wl_display_get_event_loop(wf::get_core().display);
205+
glib_timer = wl_event_loop_add_timer(evloop, glib_timer_cb, this);
206+
wl_event_source_timer_update(glib_timer, 50);
189207

190208
watch_id = DBus::watch_name(DBus::BUS_TYPE_SYSTEM, "net.hadess.SensorProxy",
191209
sigc::mem_fun(this, &WayfireAutorotateIIO::on_iio_appeared),
@@ -210,6 +228,7 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
210228
iio_proxy->signal_properties_changed().connect_notify(
211229
sigc::mem_fun(this, &WayfireAutorotateIIO::on_properties_changed));
212230
iio_proxy->call_sync("ClaimAccelerometer");
231+
update_orientation();
213232
}
214233

215234
void on_properties_changed(
@@ -228,6 +247,11 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
228247

229248
Glib::Variant<Glib::ustring> orientation;
230249
iio_proxy->get_cached_property(orientation, "AccelerometerOrientation");
250+
if (!orientation)
251+
{
252+
return;
253+
}
254+
231255
LOGI("IIO Accelerometer orientation: %s", orientation.get().c_str());
232256

233257
static const std::map<std::string, wl_output_transform> transform_by_name =
@@ -238,10 +262,55 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
238262
{"bottom-up", WL_OUTPUT_TRANSFORM_180},
239263
};
240264

241-
if (transform_by_name.count(orientation.get()))
265+
if (!transform_by_name.count(orientation.get()))
242266
{
243-
sensor_transform = transform_by_name.find(orientation.get())->second;
244-
update_transform();
267+
return;
268+
}
269+
270+
int32_t new_transform = transform_by_name.find(orientation.get())->second;
271+
272+
bool timer_running = (debounce_timer_id != 0);
273+
bool new_is_normal = (new_transform == WL_OUTPUT_TRANSFORM_NORMAL);
274+
bool pend_is_normal = (pending_transform == WL_OUTPUT_TRANSFORM_NORMAL);
275+
276+
if (!timer_running)
277+
{
278+
if (new_transform == sensor_transform)
279+
{
280+
return;
281+
}
282+
283+
pending_transform = new_transform;
284+
guint delay = new_is_normal ? DEBOUNCE_NORMAL_MS : DEBOUNCE_ROTATE_MS;
285+
debounce_timer_id = g_timeout_add(delay, [] (gpointer data) -> gboolean
286+
{
287+
auto *self = static_cast<WayfireAutorotateIIO*>(data);
288+
self->debounce_timer_id = 0;
289+
self->sensor_transform = self->pending_transform;
290+
self->update_transform();
291+
return G_SOURCE_REMOVE;
292+
}, this);
293+
} else if (!pend_is_normal)
294+
{
295+
if (!new_is_normal)
296+
{
297+
pending_transform = new_transform;
298+
}
299+
} else
300+
{
301+
if (!new_is_normal)
302+
{
303+
g_source_remove(debounce_timer_id);
304+
pending_transform = new_transform;
305+
debounce_timer_id = g_timeout_add(DEBOUNCE_ROTATE_MS, [] (gpointer data) -> gboolean
306+
{
307+
auto *self = static_cast<WayfireAutorotateIIO*>(data);
308+
self->debounce_timer_id = 0;
309+
self->sensor_transform = self->pending_transform;
310+
self->update_transform();
311+
return G_SOURCE_REMOVE;
312+
}, this);
313+
}
245314
}
246315
}
247316

@@ -259,13 +328,20 @@ class WayfireAutorotateIIO : public wf::per_output_plugin_instance_t
259328
output->rem_binding(&on_rotate_up);
260329
output->rem_binding(&on_rotate_down);
261330

331+
if (debounce_timer_id)
332+
{
333+
g_source_remove(debounce_timer_id);
334+
debounce_timer_id = 0;
335+
}
336+
262337
/* If loop is NULL, autorotate was disabled for the current output */
263338
if (loop)
264339
{
265340
iio_proxy.reset();
266341
DBus::unwatch_name(watch_id);
267342
loop->quit();
268-
output->render->rem_effect(&on_frame);
343+
wl_event_source_remove(glib_timer);
344+
glib_timer = nullptr;
269345
}
270346
}
271347
};

0 commit comments

Comments
 (0)