static void maybe_start_idle (GdkFrameClockIdle *clock_idle) { GdkFrameClockIdlePrivate *priv = clock_idle->priv; if (RUN_FLUSH_IDLE (priv) || RUN_PAINT_IDLE (priv)) { guint min_interval = 0; if (priv->min_next_frame_time != 0) { gint64 now = compute_frame_time (clock_idle); gint64 min_interval_us = MAX (priv->min_next_frame_time, now) - now; min_interval = (min_interval_us + 500) / 1000; } if (priv->flush_idle_id == 0 && RUN_FLUSH_IDLE (priv)) { priv->flush_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS + 1, min_interval, gdk_frame_clock_flush_idle, g_object_ref (clock_idle), (GDestroyNotify) g_object_unref); g_source_set_name_by_id (priv->flush_idle_id, "[gtk+] gdk_frame_clock_flush_idle"); } if (!priv->in_paint_idle && priv->paint_idle_id == 0 && RUN_PAINT_IDLE (priv)) { priv->paint_idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_REDRAW, min_interval, gdk_frame_clock_paint_idle, g_object_ref (clock_idle), (GDestroyNotify) g_object_unref); g_source_set_name_by_id (priv->paint_idle_id, "[gtk+] gdk_frame_clock_paint_idle"); } } }
static gint64 gdk_frame_clock_idle_get_frame_time (GdkFrameClock *clock) { GdkFrameClockIdlePrivate *priv = GDK_FRAME_CLOCK_IDLE (clock)->priv; gint64 computed_frame_time; /* can't change frame time during a paint */ if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE && priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS) return priv->frame_time; /* Outside a paint, pick something close to "now" */ computed_frame_time = compute_frame_time (GDK_FRAME_CLOCK_IDLE (clock)); /* 16ms is 60fps. We only update frame time that often because we'd * like to try to keep animations on the same start times. * get_frame_time() would normally be used outside of a paint to * record an animation start time for example. */ if ((computed_frame_time - priv->frame_time) > FRAME_INTERVAL) priv->frame_time = computed_frame_time; return priv->frame_time; }
static gboolean gdk_frame_clock_paint_idle (void *data) { GdkFrameClock *clock = GDK_FRAME_CLOCK (data); GdkFrameClockIdle *clock_idle = GDK_FRAME_CLOCK_IDLE (clock); GdkFrameClockIdlePrivate *priv = clock_idle->priv; gboolean skip_to_resume_events; GdkFrameTimings *timings = NULL; priv->paint_idle_id = 0; priv->in_paint_idle = TRUE; priv->min_next_frame_time = 0; skip_to_resume_events = (priv->requested & ~(GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS | GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS)) == 0 && priv->updating_count == 0; if (priv->phase > GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT) { timings = gdk_frame_clock_get_current_timings (clock); } if (!skip_to_resume_events) { switch (priv->phase) { case GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS: break; case GDK_FRAME_CLOCK_PHASE_NONE: case GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT: if (priv->freeze_count == 0) { priv->frame_time = compute_frame_time (clock_idle); _gdk_frame_clock_begin_frame (clock); timings = gdk_frame_clock_get_current_timings (clock); timings->frame_time = priv->frame_time; timings->slept_before = priv->sleep_serial != get_sleep_serial (); priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT; /* We always emit ::before-paint and ::after-paint if * any of the intermediate phases are requested and * they don't get repeated if you freeze/thaw while * in them. */ priv->requested &= ~GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT; g_signal_emit_by_name (G_OBJECT (clock), "before-paint"); priv->phase = GDK_FRAME_CLOCK_PHASE_UPDATE; } /* fallthrough */ case GDK_FRAME_CLOCK_PHASE_UPDATE: if (priv->freeze_count == 0) { if ((priv->requested & GDK_FRAME_CLOCK_PHASE_UPDATE) != 0 || priv->updating_count > 0) { priv->requested &= ~GDK_FRAME_CLOCK_PHASE_UPDATE; g_signal_emit_by_name (G_OBJECT (clock), "update"); } } /* fallthrough */ case GDK_FRAME_CLOCK_PHASE_LAYOUT: if (priv->freeze_count == 0) { int iter; #ifdef G_ENABLE_DEBUG if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) { if (priv->phase != GDK_FRAME_CLOCK_PHASE_LAYOUT && (priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT)) timings->layout_start_time = g_get_monotonic_time (); } #endif /* G_ENABLE_DEBUG */ priv->phase = GDK_FRAME_CLOCK_PHASE_LAYOUT; /* We loop in the layout phase, because we don't want to progress * into the paint phase with invalid size allocations. This may * happen in some situation like races between user window * resizes and natural size changes. */ iter = 0; while ((priv->requested & GDK_FRAME_CLOCK_PHASE_LAYOUT) && priv->freeze_count == 0 && iter++ < 4) { priv->requested &= ~GDK_FRAME_CLOCK_PHASE_LAYOUT; g_signal_emit_by_name (G_OBJECT (clock), "layout"); } if (iter == 5) g_warning ("gdk-frame-clock: layout continuously requested, giving up after 4 tries"); } /* fallthrough */ case GDK_FRAME_CLOCK_PHASE_PAINT: if (priv->freeze_count == 0) { #ifdef G_ENABLE_DEBUG if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) { if (priv->phase != GDK_FRAME_CLOCK_PHASE_PAINT && (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT)) timings->paint_start_time = g_get_monotonic_time (); } #endif /* G_ENABLE_DEBUG */ priv->phase = GDK_FRAME_CLOCK_PHASE_PAINT; if (priv->requested & GDK_FRAME_CLOCK_PHASE_PAINT) { priv->requested &= ~GDK_FRAME_CLOCK_PHASE_PAINT; g_signal_emit_by_name (G_OBJECT (clock), "paint"); } } /* fallthrough */ case GDK_FRAME_CLOCK_PHASE_AFTER_PAINT: if (priv->freeze_count == 0) { priv->requested &= ~GDK_FRAME_CLOCK_PHASE_AFTER_PAINT; g_signal_emit_by_name (G_OBJECT (clock), "after-paint"); /* the ::after-paint phase doesn't get repeated on freeze/thaw, */ priv->phase = GDK_FRAME_CLOCK_PHASE_NONE; #ifdef G_ENABLE_DEBUG if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) timings->frame_end_time = g_get_monotonic_time (); #endif /* G_ENABLE_DEBUG */ } /* fallthrough */ case GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS: ; } } #ifdef G_ENABLE_DEBUG if ((_gdk_debug_flags & GDK_DEBUG_FRAMES) != 0) { if (timings && timings->complete) _gdk_frame_clock_debug_print_timings (clock, timings); } #endif /* G_ENABLE_DEBUG */ if (priv->requested & GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS) { priv->requested &= ~GDK_FRAME_CLOCK_PHASE_RESUME_EVENTS; g_signal_emit_by_name (G_OBJECT (clock), "resume-events"); } if (priv->freeze_count == 0) priv->phase = GDK_FRAME_CLOCK_PHASE_NONE; priv->in_paint_idle = FALSE; /* If there is throttling in the backend layer, then we'll do another * update as soon as the backend unthrottles (if there is work to do), * otherwise we need to figure when the next frame should be. */ if (priv->freeze_count == 0) { priv->min_next_frame_time = compute_min_next_frame_time (clock_idle, priv->frame_time); maybe_start_idle (clock_idle); } if (priv->freeze_count == 0) priv->sleep_serial = get_sleep_serial (); return FALSE; }