Skip to content

Commit

Permalink
Improve drawing of visualstimuli
Browse files Browse the repository at this point in the history
Stimuli can now be drawn again. fixes #142
Additionally implement the Psy.Canvas::draw signal on
the canvas. In order to initiate a draw one should
call [[email protected]_draw]. This will emit
the draw signal on the canvas, the default handler
will draw the canvas.

With these changes the Psy.GtkWindow will have
correct window dimensions once it is constructed and will
not depend on the resize signal to have it's correct
dimensions For psylib aims at having non resizable
windows. This fixes #97
  • Loading branch information
maartenuni committed Feb 5, 2025
1 parent f5c5e46 commit 61edef7
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 53 deletions.
5 changes: 2 additions & 3 deletions psy/backend_gtk/psy-gtk-window.c
Original file line number Diff line number Diff line change
Expand Up @@ -118,16 +118,15 @@ 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);
gint64 frame_count = gdk_frame_timings_get_frame_counter(timings);

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);
Expand Down
27 changes: 25 additions & 2 deletions psy/gl/psy-gl-canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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),
Expand Down
4 changes: 2 additions & 2 deletions psy/hw/psy-parallel-port.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.
*/

Expand Down
101 changes: 59 additions & 42 deletions psy/psy-canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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.
* [[email protected]_connect] you'll can get the canvas before drawing is
* initiated, but if you use [[email protected]_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);
}

/**
Expand Down Expand Up @@ -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);
}
6 changes: 6 additions & 0 deletions psy/psy-canvas.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
14 changes: 10 additions & 4 deletions psy/psy-image-canvas.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down Expand Up @@ -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)
Expand All @@ -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);
Expand Down

0 comments on commit 61edef7

Please sign in to comment.