/* * testcard.c * * Copyright © 2013-2016 Thomas White * * This file is part of Colloquium. * * Colloquium is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "inhibit_screensaver.h" #include "presentation.h" struct testcard { GtkWidget *window; char geom[256]; int slide_width; int slide_height; GtkWidget *drawingarea; struct inhibit_sys *inhibit; struct presentation *p; }; static gint destroy_sig(GtkWidget *widget, struct testcard *tc) { free(tc); return FALSE; } static void arrow_left(cairo_t *cr, double size) { cairo_rel_line_to(cr, size, size); cairo_rel_line_to(cr, 0.0, -2*size); cairo_rel_line_to(cr, -size, size); } static void arrow_right(cairo_t *cr, double size) { cairo_rel_line_to(cr, -size, size); cairo_rel_line_to(cr, 0.0, -2*size); cairo_rel_line_to(cr, size, size); } static void arrow_down(cairo_t *cr, double size) { cairo_rel_line_to(cr, -size, -size); cairo_rel_line_to(cr, 2*size, 0.0); cairo_rel_line_to(cr, -size, size); } static void arrow_up(cairo_t *cr, double size) { cairo_rel_line_to(cr, -size, size); cairo_rel_line_to(cr, 2*size, 0.0); cairo_rel_line_to(cr, -size, -size); } static void colour_box(cairo_t *cr, double x, double y, double r, double g, double b, const char *label) { const double w = 50.0; const double h = 50.0; cairo_text_extents_t size; cairo_rectangle(cr, x+0.5, y+0.5, w, h); cairo_set_source_rgb(cr, r, g, b); cairo_fill_preserve(cr); cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); cairo_set_line_width(cr, 1.0); cairo_stroke(cr); cairo_set_font_size(cr, 24.0); cairo_text_extents(cr, label, &size); cairo_move_to(cr, x+(w/2.0)-(size.width/2.0), y-10.0); cairo_show_text(cr, label); } static gboolean draw_sig(GtkWidget *da, cairo_t *cr, struct testcard *tc) { double xoff, yoff; double width, height; int h; PangoLayout *pl; PangoFontDescription *desc; char tmp[1024]; int plw, plh; double xp, yp; width = gtk_widget_get_allocated_width(GTK_WIDGET(da)); height = gtk_widget_get_allocated_height(GTK_WIDGET(da)); /* Overall background */ cairo_rectangle(cr, 0.0, 0.0, width, height); cairo_set_source_rgb(cr, 0.0, 0.0, 1.0); cairo_fill(cr); /* FIXME: Assumes that monitor and slide sizes are such that * letterboxing at sides. This needn't be the case. */ h = tc->slide_width * tc->p->slide_height / tc->p->slide_width; /* Get the overall size */ xoff = (width - tc->slide_width)/2.0; yoff = (height - h)/2.0; /* Background of slide */ cairo_rectangle(cr, xoff, yoff, tc->slide_width, h); cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); cairo_fill(cr); /* Arrows showing edges of screen */ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); cairo_move_to(cr, 0.0, height/2); arrow_left(cr, 100.0); cairo_fill(cr); cairo_move_to(cr, width, height/2); arrow_right(cr, 100.0); cairo_fill(cr); cairo_move_to(cr, width/2, height); arrow_down(cr, 100.0); cairo_fill(cr); cairo_move_to(cr, width/2, 0.0); arrow_up(cr, 100.0); cairo_fill(cr); /* Arrows showing edges of slide */ cairo_translate(cr, xoff, yoff); cairo_set_source_rgb(cr, 0.5, 0.0, 0.0); cairo_move_to(cr, 0.0, 100+h/2); arrow_left(cr, 80.0); cairo_fill(cr); cairo_move_to(cr, tc->slide_width, 100+h/2); arrow_right(cr, 80.0); cairo_fill(cr); cairo_move_to(cr, 100+tc->slide_width/2, h); arrow_down(cr, 80.0); cairo_fill(cr); cairo_move_to(cr, 100+tc->slide_width/2, 0.0); arrow_up(cr, 80.0); cairo_fill(cr); /* Stuff in the middle */ yp = (tc->slide_height-400)/2.0; cairo_save(cr); cairo_translate(cr, 0.0, yp); snprintf(tmp, 1024, "Colloquium "PACKAGE_VERSION" test card\n" "Screen resolution %.0f × %.0f\n" "Slide resolution %i × %i", width, height, tc->slide_width, h); pl = pango_cairo_create_layout(cr); desc = pango_font_description_from_string("Sans 24"); pango_layout_set_font_description(pl, desc); pango_layout_set_text(pl, tmp, -1); pango_layout_get_size(pl, &plw, &plh); plw = pango_units_to_double(plw); plh = pango_units_to_double(plh); cairo_move_to(cr, (tc->slide_width-plw)/2, 0.0); cairo_set_source_rgb(cr, 0.0, 0.0, 0.0); pango_cairo_show_layout(cr, pl); /* Colour boxes */ xp = (tc->slide_width-450)/2.0; colour_box(cr, xp+0, 200, 1.0, 0.0, 0.0, "Red"); colour_box(cr, xp+80, 200, 0.0, 1.0, 0.0, "Green"); colour_box(cr, xp+160, 200, 0.0, 0.0, 1.0, "Blue"); colour_box(cr, xp+240, 200, 1.0, 1.0, 0.0, "Yellow"); colour_box(cr, xp+320, 200, 1.0, 0.0, 1.0, "Pink"); colour_box(cr, xp+400, 200, 0.0, 1.0, 1.0, "Cyan"); /* Shades of grey */ double i; for ( i=0; i<=1.0; i+=0.2 ) { char label[32]; snprintf(label, 31, "%.0f%%", i*100.0); colour_box(cr, xp+(i*5*80), 300, i, i, i, label); } cairo_restore(cr); return FALSE; } static void size_sig(GtkWidget *widget, GdkRectangle *rect, struct testcard *ss) { int w; w = rect->height * ss->p->slide_width/ss->p->slide_height; if ( w > rect->width ) w = rect->width; ss->slide_width = w; ss->slide_height = rect->height; } static gboolean key_press_sig(GtkWidget *da, GdkEventKey *event, struct testcard *tc) { if ( !event->is_modifier ) gtk_widget_destroy(tc->window); return FALSE; } void show_testcard(struct presentation *p) { GdkDisplay *display; int n_monitors; struct testcard *tc; tc = calloc(1, sizeof(struct testcard)); if ( tc == NULL ) return; tc->p = p; if ( tc->inhibit == NULL ) { tc->inhibit = inhibit_prepare(); } tc->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); tc->drawingarea = gtk_drawing_area_new(); gtk_container_add(GTK_CONTAINER(tc->window), tc->drawingarea); gtk_widget_set_can_focus(GTK_WIDGET(tc->drawingarea), TRUE); gtk_widget_add_events(GTK_WIDGET(tc->drawingarea), GDK_KEY_PRESS_MASK); g_signal_connect(G_OBJECT(tc->drawingarea), "key-press-event", G_CALLBACK(key_press_sig), tc); g_signal_connect(G_OBJECT(tc->window), "destroy", G_CALLBACK(destroy_sig), tc); g_signal_connect(G_OBJECT(tc->window), "size-allocate", G_CALLBACK(size_sig), tc); g_signal_connect(G_OBJECT(tc->drawingarea), "draw", G_CALLBACK(draw_sig), tc); gtk_widget_grab_focus(GTK_WIDGET(tc->drawingarea)); display = gdk_display_get_default(); n_monitors = gdk_display_get_n_monitors(display); GdkMonitor *mon_ss; if ( n_monitors == 1 ) { mon_ss = gdk_display_get_primary_monitor(display); printf("Single monitor mode\n"); } else { mon_ss = gdk_display_get_monitor(display, 1); printf("Dual monitor mode\n"); } /* Workaround because gtk_window_fullscreen_on_monitor doesn't work */ GdkRectangle rect; gdk_monitor_get_geometry(mon_ss, &rect); gtk_window_move(GTK_WINDOW(tc->window), rect.x, rect.y); gtk_window_fullscreen(GTK_WINDOW(tc->window)); gtk_widget_show_all(GTK_WIDGET(tc->window)); if ( tc->inhibit != NULL ) do_inhibit(tc->inhibit, 1); }