Meltdown – The Computer Lab Prank

I remember that little prank from the days I was a student. You work on an X terminal, and out of the blue, all the display contents gradually disappear’ Pixel after pixel turns black. But don’t worry – you’ll regain control over your display shortly. shortly.
Everyone can access other X terminal display, and mess with it.

How Does It Work?

This program is a simple one using the GDK library, Gnome’s window management package. Including ‘gdk.h’ will also include:

The Program’s Flow

The main function of the program performs the following steps:
1. Initialize GDK.
2. Create a window whose dimensions are the same as those of the root window.
3. Make the window’s background transparent.
4. Make the window a full-screen window.
5. Add an event handler. to handle Expose events.
The event handler will perform the following steps:
1. Create a list of columns and lengths (number of blackened pixels).
2. Create the Graphics Context for the window.
3. Blacken pixels until all pixels are black.
4. Quit the main loop.

Includes And Structures:

#include <stdio.h>
#include <stdlib.h>  
#include <gdk/gdk.h>

GMainLoop *mainloop;
GList *list;

typedef struct col_and_length_t{
  short col;  // Column number
  short len;  // Number of blackened pixels.
} col_and_length;`

The main function:

int main(int argc, char *argv[]){
  gdk_init(NULL, NULL);
  GdkDisplay *disp=gdk_display_get_default();
  GdkScreen *scr = gdk_display_get_default_screen (disp);
  GdkWindow *root = gdk_screen_get_root_window(scr);
  int rootWidth = gdk_window_get_width(root);
  int rootHeight = gdk_window_get_height(root);
  GdkWindowAttr attr;
  attr.width=rootWidth;
  attr.height=rootHeight;
  attr.x=0;
  attr.y=0;
  attr.window_type = GDK_WINDOW_TOPLEVEL;
  attr.wclass=GDK_INPUT_OUTPUT;

  GdkWindow *newWin=gdk_window_new(root,&attr, GDK_WA_X | GDK_WA_Y);
  gdk_event_handler_set (eventFunc, newWin, NULL);
  GdkRGBA color;
  color.alpha=0;

  gdk_window_set_background_rgba(newWin, &color);
  gdk_window_fullscreen(newWin);
  gdk_event_handler_set (eventFunc, newWin, NULL);
  gdk_window_show(newWin);
  mainloop = g_main_new (TRUE);
  g_main_loop_run (mainloop);
  gdk_display_close(disp);

return 0;
}

The event handler

void start_meltdown(GdkWindow *newWin, int height){
  cairo_t *gc=gdk_cairo_create(newWin);
  cairo_set_line_width(gc,2);
  cairo_set_source_rgb (gc, 0, 0, 0);
  int cell_no,size;
  GList *link;
  col_and_length *link_data;
  size=g_list_length(list);

  while(size>0){
    cell_no=random() % size;
    link = g_list_nth(list,cell_no);
    link_data = (col_and_length *)link->data;
    cairo_move_to(gc, link_data->col, link_data->len);
    cairo_rel_line_to(gc, 0, 1);
    cairo_stroke(gc);
    link_data->len++;
    if (link_data->len >= height){
      list=g_list_remove_link(list, link);
      --size;
    }
  }
  g_main_loop_quit(mainloop);
}

void eventFunc(GdkEvent *evt, gpointer data){
  GdkWindow *newWin = (GdkWindow *)data;
  if (gdk_event_get_event_type(evt) == GDK_EXPOSE && gdk_event_get_window (evt) == newWin){
    int width=gdk_window_get_width(newWin);
    int height=gdk_window_get_height(newWin);
    int i;
    for (i=0; i<width;i++){
      col_and_length *cell=(col_and_length *)calloc(sizeof(col_and_length), 1);
      cell->col=i;
      cell->len=0;
      list = g_list_append(list, cell);
    }
    start_meltdown(newWin,height);
  }

}

Compiling

In linux, compiling a program is easy thanks to the pkg-config command.
Run the following from the command line:

gcc meltdown.c `pkg-config --cflags --libs gdk-3.0` -o meltdown

Now, to run the program type:

./meltdown

Written with StackEdit.

Advertisements

Creating Custom Cursors With GDK-Pixbuf-2 And Cairo

Wait! Why use GDK-2 when there ate later stable versions you can download for free? Sometimes you use a tool – such as OpenCV – that integrates with GTK-2, and GDK-2.

Your operating system may allow you to create custom cursor, or mouse pointers, of your own with colors and an alpha channel (opacity). GDK allows you to create a cursor from a pixel buffer (pixbuf), using the function ‘gdk_cursor_new_from_pixbuf‘.  OK, but first let’s make a pixel buffer. In the documentation of GDK 3, I found 2 functions to create a pixbuf: gdk_pixbuf_get_from_window and gdk_pixbuf_get_from_surface. The latter does not exist in GDK-2. However, there is one method, I haven’t found documented` It’s called gdk_pixbuf_new_from_data.  You can get the data from Cairo after you’ve drawn your image using Cairo.

Using Cairo might be a bit confusing because::

  • Cairo uses two data types for color channels: double-precision floating-point numbers and unsigned 8-bit integers.
  • The color of the cursor is not as intended when calling ‘cairo_set_source_rgba’.

The following function in C will demonstrate what I mean:

Here, when the cursor is created its color is yellow, but if you save your created image to a file it will be cyan.

Yellow cursor over the red square.
Yellow cursor over the red square.

The image when saved to a PNG file

The image when saved to a PNG file

void createCursor(){
    double arcEnd=2*M_PI;
    GdkPixbuf *pixbuf;
    int stride=cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, 10); // Will be used later, when we'll create the pixbuf.
    cairo_surface_t *surf=cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10); // A canvas of width 10 and height 10.
    cairo_t *cairo_ctx = cairo_create(surf);
    cairo_set_source_rgba(cairo_ctx, 0., 1., 1., 1.); // Double precision values for Red, Green, Blue and Alpha channels between 0 and 1.

    cairo_arc(cairo_ctx,5,5,4,0,arcEnd); // Draw a circle or an arc of a circle whose center is at (5,5) and whose radius is 4
                                         // The arc begins at angle 0 and ends in angle 2Π
    cairo_fill(cairo_ctx); // Fill the circle with cyan, though we've intended to create a yellow one.

    // Create an example image for the blog
    cairo_surface_write_to_png(surf, "/tmp/fortheblog.png");

    guchar *data = cairo_image_surface_get_data(surf); // The data is in BGR, reverse order of RGB !!!

    pixbuf = gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, true, 8, 10, 10, stride, NULL, NULL); // Stride is the difference in bytes between two
                                                                                                      // consecutive rows.
    cursor=gdk_cursor_new_from_pixbuf(gdk_display_get_default(), pixbuf, 5, 5);

}

“cursor” in this function is a global variable of type “GdkCursor *”.Now, to add the cursor to a window, use gdk_window_set_cursor.

The “gtk_pixbuf_new_from_data” Function.

This function uses the data returned from the function “cairo_image_surface_get_data”. The data is a pointer to a character. The function receiving the data doesn’t know how many characters are referred to, and how to split it into rows and columns. Following is its prototype:

GdkPixbuf *gdk_pixbuf_new_from_data (const guchar *data,
                     GdkColorspace colorspace,
                     gboolean has_alpha,
                     int bits_per_sample,
                     int width, int height,
                     int rowstride,
                     GdkPixbufDestroyNotify destroy_fn,
                     gpointer destroy_fn_data);