diff --git a/psy/backend_gtk/psy-gtk-window.c b/psy/backend_gtk/psy-gtk-window.c index 82b1125..ce3f7f7 100644 --- a/psy/backend_gtk/psy-gtk-window.c +++ b/psy/backend_gtk/psy-gtk-window.c @@ -118,8 +118,6 @@ tick_callback(GtkWidget *d_area, GdkFrameClock *clock, gpointer data) return G_SOURCE_REMOVE; } - PsyCanvasClass *canvas_class = PSY_CANVAS_GET_CLASS(window); - GdkFrameTimings *timings = gdk_frame_clock_get_current_timings(clock); gint64 predicted = gdk_frame_timings_get_predicted_presentation_time(timings); @@ -127,7 +125,8 @@ tick_callback(GtkWidget *d_area, GdkFrameClock *clock, gpointer data) PsyTimePoint *tp = psy_time_point_new_monotonic(predicted); - canvas_class->draw(PSY_CANVAS(window), frame_count, tp); + // canvas_class->draw(PSY_CANVAS(window), frame_count, tp); + psy_canvas_begin_draw(PSY_CANVAS(window), frame_count, tp); psy_gtk_window_compute_frame_stats(window, tp); psy_gtk_window_set_last_frame_time(window, tp); diff --git a/psy/gl/psy-gl-canvas.c b/psy/gl/psy-gl-canvas.c index 229cad8..831678f 100644 --- a/psy/gl/psy-gl-canvas.c +++ b/psy/gl/psy-gl-canvas.c @@ -327,13 +327,22 @@ gl_canvas_draw(PsyCanvas *canvas, guint64 frame_num, PsyTimePoint *tp) } static void -gl_canvas_clear(PsyCanvas *self) +gl_canvas_clear(PsyCanvas *canvas) { // don't chain up, its not implemented in parent + PsyGlCanvas *self = PSY_GL_CANVAS(canvas); gfloat r, b, g, a; GError *error = NULL; - PsyColor *color = psy_canvas_get_background_color(self); + PsyColor *color = psy_canvas_get_background_color(canvas); + + if (eglMakeCurrent( + self->display, self->surface, self->surface, self->egl_context) + != EGL_TRUE) { + EGLint error = eglGetError(); + g_critical("Unable to make GlCanvas current: %s", + psy_egl_strerr(error)); + } // clang-format off g_object_get(color, @@ -383,6 +392,20 @@ gl_canvas_get_image(PsyCanvas *canvas) g_critical("Unable to make context current: %s", psy_egl_strerr(error)); } + glFlush(); + psy_gl_check_error(&error); + if (error) { + g_critical("Unable to flush drawing commands before reading pixels"); + return NULL; + } + + glFinish(); + psy_gl_check_error(&error); + if (error) { + g_critical("Unable to finish drawing before reading pixels"); + return NULL; + } + glReadPixels(0, 0, psy_canvas_get_width(canvas), diff --git a/psy/hw/psy-parallel-port.c b/psy/hw/psy-parallel-port.c index 14f54a7..e5ae70f 100644 --- a/psy/hw/psy-parallel-port.c +++ b/psy/hw/psy-parallel-port.c @@ -56,7 +56,7 @@ psy_parallel_port_info_copy(PsyParallelPortInfo *self) * psy_parallel_port_info_free:(skip) * @self: the parameter to free * - * Frees the instance of [class@ParallelPortInfo]. + * Frees the instance of [struct@ParallelPortInfo]. */ void psy_parallel_port_info_free(PsyParallelPortInfo *self) @@ -126,7 +126,7 @@ G_DEFINE_BOXED_TYPE(PsyParallelPortInfo, * needs to be designed that can read, write, open, close in an async fashion. * * PsyParallel is implemented fully by the classes PsyParport (Linux) and - * PsyInpoutPort (windows). Using [ctor@PsyParrallelPort.new], you'll get + * PsyInpoutPort (windows). Using [ctor@Psy.ParallelPort.new], you'll get * the device that is appropriate on your os, or NULL when not available. */ diff --git a/psy/psy-canvas.c b/psy/psy-canvas.c index 5c1b754..a710272 100644 --- a/psy/psy-canvas.c +++ b/psy/psy-canvas.c @@ -61,8 +61,7 @@ typedef struct PsyCanvasPrivate { G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(PsyCanvas, psy_canvas, G_TYPE_OBJECT) typedef enum { - CLEAR, - DRAW_STIMULI, + DRAW, RESIZE, LAST_SIGNAL, } PsyCanvasSignal; @@ -779,6 +778,14 @@ psy_canvas_class_init(PsyCanvasClass *klass) g_object_class_install_properties(object_class, N_PROPS, obj_properties); + /** + * PsyCanvas::resize + * @self: an instance of [class@Canvas] + * @width: the new width of the canvas + * @height: the new height of the canvas + * + * This signal is called when the canvas is resized. + */ canvas_signals[RESIZE] = g_signal_new("resize", PSY_TYPE_CANVAS, @@ -792,46 +799,33 @@ psy_canvas_class_init(PsyCanvasClass *klass) G_TYPE_INT, G_TYPE_INT); - // /** - // * PsyCanvas::clear - // * - // * This is the first action that is run when a new frame should be - // * presented. The default handler calls the private/protected clear - // function - // * that clears the canvas. - // */ - // canvas_signals[CLEAR] = g_signal_new( - // "clear", - // G_TYPE_FROM_CLASS(object_class), - // G_SIGNAL_RUN_FIRST, - // G_STRUCT_OFFSET(PsyCanvasClass, clear), - // NULL, - // NULL, - // NULL, - // G_TYPE_NONE, - // 0); - // - // /** - // * PsyCanvas::draw-stimuli - // * - // * This is the first action that is run when a new frame should be - // * presented. The default handler calls the private/protected clear - // function - // * that clears the canvas. - // */ - // canvas_signals[DRAW_STIMULI] = g_signal_new( - // "draw-stimuli", - // G_TYPE_FROM_CLASS(object_class), - // G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE, - // G_STRUCT_OFFSET(PsyCanvasClass, clear), - // NULL, - // NULL, - // NULL, - // G_TYPE_NONE, - // 2, - // G_TYPE_UINT64, - // PSY_TYPE_TIME_POINT - // ); + /** + * PsyCanvas::draw + * @self: an instance of [class@Canvas] + * @frame_num: the number of the frame + * @frame_time: the time when this frame should be visible + * + * This is the first action that is run when a new frame should be + * presented. The default handler calls the private/protected clear function + * that clears the canvas. + * + * When this signal is emitted, the frame is drawn by the default handler + * for this signal. So if you connect normally e.g. + * [func@GObject.signal_connect] you'll can get the canvas before drawing is + * initiated, but if you use [func@GObject.signal_connect_after], the canvas + * should reflect the result after drawing has completed. + */ + canvas_signals[DRAW] = g_signal_new("draw", + G_TYPE_FROM_CLASS(object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET(PsyCanvasClass, draw), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 2, + G_TYPE_UINT64, + PSY_TYPE_TIME_POINT); } /** @@ -1476,3 +1470,26 @@ psy_canvas_reset(PsyCanvas *self) cls->reset(self); } + +/** + * psy_canvas_begin_draw:(skip) + * @canvas: the instance of [class@Canvas] that should be drawn for a given + * frame + * @frame_num: the nth frame that will be drawn + * @frame_time:(transfer none): The timepoint of the new frame + * + * This function is called internally by psylib, its purpose is to draw one + * specific frame. It causes the [signal@Canvas::draw] to be emitted. + * + * Stability: private + */ +void +psy_canvas_begin_draw(PsyCanvas *self, + guint64 frame_num, + PsyTimePoint *frame_time) +{ + g_return_if_fail(PSY_IS_CANVAS(self)); + g_return_if_fail(frame_time); + + g_signal_emit(self, canvas_signals[DRAW], 0, frame_num, frame_time); +} diff --git a/psy/psy-canvas.h b/psy/psy-canvas.h index 7f56a2e..e9a8579 100644 --- a/psy/psy-canvas.h +++ b/psy/psy-canvas.h @@ -184,4 +184,10 @@ psy_canvas_get_num_frames_total(PsyCanvas *self); G_MODULE_EXPORT void psy_canvas_reset(PsyCanvas *self); +/* functions private to psylib */ +void +psy_canvas_begin_draw(PsyCanvas *canvas, + guint64 frame_num, + PsyTimePoint *frame_time); + G_END_DECLS diff --git a/psy/psy-image-canvas.c b/psy/psy-image-canvas.c index 64c06c1..5e220bc 100644 --- a/psy/psy-image-canvas.c +++ b/psy/psy-image-canvas.c @@ -131,13 +131,13 @@ image_canvas_iterate(PsyImageCanvas *self) PsyDuration *dur = psy_canvas_get_frame_dur(PSY_CANVAS(self)); PsyTimePoint *new_time = psy_time_point_add(priv->time, dur); - psy_image_canvas_set_time(self, new_time); + psy_image_canvas_set_time(self, new_time); // image now owns new_time if (priv->auto_iterate) { psy_timer_set_fire_time(priv->iter_timer, new_time); } - PSY_CANVAS_GET_CLASS(self)->draw(PSY_CANVAS(self), nf + 1, new_time); + psy_canvas_begin_draw(PSY_CANVAS(self), nf + 1, new_time); } /** @@ -307,8 +307,10 @@ psy_image_canvas_get_time(PsyImageCanvas *self) * assumes a valid frame dur has been set on the [class@Canvas]. * * When this object is set, the object will start to iterate itself based on the - * duration of the PsyCanvas. This means the [property@ImageCanvas:time] will - * increase for each iteration. + * frame duration of the PsyCanvas. This means the [property@ImageCanvas:time] + * will increase for each iteration. + * When iterate is true, the current time of the image canvas will be set to + * the current time, so the next update should be roughly one frame away. */ void psy_image_canvas_set_auto_iterate(PsyImageCanvas *self, gboolean iterate) @@ -324,6 +326,10 @@ psy_image_canvas_set_auto_iterate(PsyImageCanvas *self, gboolean iterate) PsyDuration *frame_dur = psy_canvas_get_frame_dur(PSY_CANVAS(self)); g_return_if_fail(frame_dur != NULL); + PsyClock *clk = psy_clock_new(); + psy_image_canvas_set_time(self, psy_clock_now(clk)); + psy_clock_free(clk); + PsyTimePoint *new_frame_tp = psy_time_point_add(priv->time, frame_dur); psy_timer_set_fire_time(priv->iter_timer, new_frame_tp); psy_time_point_free(new_frame_tp);