Newbie Issue with X11 Double buffering - linux

I botched together the following code to do a simple rotating radar display.
Q1) How can I eliminate the flicker of the line being drawn and then drawn over. Can I use double buffering somehow ?
Q2) How can I get Mouse and Keyborad inputs and process/parse them.
My goal is to read angle and distance data from an ultrasonic sensor and display that as a sweep. This data comes from an Arduino with an ultrasonic rangefinder mounted on a servo sweeping backwards and forwards 180 Deg. I also cant quite get the math right. I'd like it to go from 0 to 180, left to right and back depending on the angle data from the Arduino. 0 Deg is 90 Deg off to the right. So I know I have to offset it by 180 Deg but am hopeless at math. The sweep would have to start at 270 Deg and sweep through to 0 Deg. I will have to learn how to read the serial port and parse the data for the display.
Any help would be appreciated.
Pls don't jump on me. Just trying to learn.
Here is my code. I used a tutorial as a base and kind of guessed my way through.
CODE:
/*
* simple-drawing.c - demonstrate drawing of pixels, lines, arcs, etc.
* on a window. All drawings are done in black color
* over a white background.
*/
#include <X11/Xlib.h>
#include <stdio.h>
#include <stdlib.h> /* getenv(), etc. */
#include <unistd.h> /* sleep(), etc. */
// Include math library functions.
#include <math.h>
/*
* function: create_simple_window. Creates a window with a white background
* in the given size.
* input: display, size of the window (in pixels), and location of the window
* (in pixels).
* output: the window's ID.
* notes: window is created with a black border, 2 pixels wide.
* the window is automatically mapped after its creation.
*/
Window
create_simple_window(Display* display, int width, int height, int x, int y)
{
int screen_num = DefaultScreen(display);
int win_border_width = 2;
Window win;
/* create a simple window, as a direct child of the screen's */
/* root window. Use the screen's black and white colors as */
/* the foreground and background colors of the window, */
/* respectively. Place the new window's top-left corner at */
/* the given 'x,y' coordinates. */
win = XCreateSimpleWindow(display, RootWindow(display, screen_num),
x, y, width, height, win_border_width,
BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
/* make the window actually appear on the screen. */
XMapWindow(display, win);
/* flush all pending requests to the X server. */
XFlush(display);
return win;
}
GC
create_gc(Display* display, Window win, int reverse_video)
{
GC gc; /* handle of newly created GC. */
unsigned long valuemask = 0; /* which values in 'values' to */
/* check when creating the GC. */
XGCValues values; /* initial values for the GC. */
unsigned int line_width = 2; /* line width for the GC. */
int line_style = LineSolid; /* style for lines drawing and */
int cap_style = CapButt; /* style of the line's edje and */
int join_style = JoinBevel; /* joined lines. */
int screen_num = DefaultScreen(display);
gc = XCreateGC(display, win, valuemask, &values);
if (gc < 0) {
fprintf(stderr, "XCreateGC: \n");
}
/* allocate foreground and background colors for this GC. */
if (reverse_video) {
XSetForeground(display, gc, WhitePixel(display, screen_num));
XSetBackground(display, gc, BlackPixel(display, screen_num));
}
else {
XSetForeground(display, gc, BlackPixel(display, screen_num));
XSetBackground(display, gc, WhitePixel(display, screen_num));
}
/* define the style of lines that will be drawn using this GC. */
XSetLineAttributes(display, gc,
line_width, line_style, cap_style, join_style);
/* define the fill style for the GC. to be 'solid filling'. */
XSetFillStyle(display, gc, FillSolid);
return gc;
}
void
main(int argc, char* argv[])
{
Display* display; /* pointer to X Display structure. */
int screen_num; /* number of screen to place the window on. */
Window win; /* pointer to the newly created window. */
unsigned int display_width,
display_height; /* height and width of the X display. */
unsigned int width, height; /* height and width for the new window. */
char *display_name = getenv("DISPLAY"); /* address of the X display. */
GC gc; /* GC (graphics context) used for drawing */
/* in our window. */
// Added code to select simple black or white color to draw.
Colormap screen_colormap; /* color map to use for allocating colors. */
XColor black, white;
/* used for allocation of the given color */
/* map entries. */
Status rc; /* return status of various Xlib functions. */
// Johns stuff. This code draws a line from starting co-ordinates at an angle
//
int angle=0;
int x1,x2,y1,y2;
int px,py;
// End
/* open connection with the X server. */
display = XOpenDisplay(display_name);
if (display == NULL) {
fprintf(stderr, "%s: cannot connect to X server '%s'\n",
argv[0], display_name);
exit(1);
}
/* get the geometry of the default screen for our display. */
screen_num = DefaultScreen(display);
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
/* make the new window occupy 1/9 of the screen's size. */
width = (display_width / 2);
height = (display_height / 2);
printf("window width - '%d'; height - '%d'\n", width, height);
/* create a simple window, as a direct child of the screen's */
/* root window. Use the screen's white color as the background */
/* color of the window. Place the new window's top-left corner */
/* at the given 'x,y' coordinates. */
win = create_simple_window(display, width, height, 50, 50);
/* allocate a new GC (graphics context) for drawing in the window. */
gc = create_gc(display, win, 0);
XSync(display, False);
// Following 3 lines needed or else we just see white background.
/* catch expose events */
XSelectInput(display, win, ExposureMask);
/* wait for the expose event */
XEvent ev;
XNextEvent(display, &ev);
// Follwing code borrowed to get colors to draw.
/* get access to the screen's color map. */
screen_colormap = DefaultColormap(display, DefaultScreen(display));
/* allocate the set of colors we will want to use for the drawing. */
rc = XAllocNamedColor(display, screen_colormap, "black", &black, &black);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - failed to allocated 'black' color.\n");
exit(1);
}
rc = XAllocNamedColor(display, screen_colormap, "white", &white, &white);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - failed to allocated 'white' color.\n");
exit(1);
}
/* draw one pixel near each corner of the window */
XDrawPoint(display, win, gc, 5, 5);
XDrawPoint(display, win, gc, 5, height-5);
XDrawPoint(display, win, gc, width-5, 5);
XDrawPoint(display, win, gc, width-5, height-5);
// Do our display. Loop forever because I don't know how to process keystrokes.
// If we start from the center of the display, 0 is to the right (E), 90 is to the
// bottom (S), 180 (W),270 (N). Don't quite know how to "rotate" the "beam" 180 Deg.
// Needs double buffering to remove the flicker.
// I would like to make a seperate subroutine eg: DrawRadar( int angle, int distance);
x1=width / 2; //Find center of display.
y1=height / 2;
px=x1; // Previous X and Y
py=y1;
while(1){
for(angle=0; angle < 360; angle++)
{
// Distance set to 150 pixels. Radar returns value up to 3300 mm.
// Will have to scale down.
x2=x1 + cos((angle * M_PI) / 180) * 150; // Must convert to radians.
y2=y1 + sin((angle * M_PI) / 180) * 150; // " " " "
XSetForeground(display, gc, black.pixel); //
XDrawLine(display, win, gc, x1, y1, x2, y2);
px=x2;
py=y2;
XFlush(display); // Display what we've drawn.
usleep(3500); // Wait so eye can see it.
XSetForeground(display, gc, white.pixel); // Rub out the line we just drew.
XDrawLine(display, win, gc, x1, y1, px ,py);
XFlush(display); // Causes flickering but only way at the moment.
XSetForeground(display, gc, black.pixel);
usleep(3500);
}
}
// End. Never get past here.
/* flush all pending requests to the X server. */
XFlush(display);
XSync(display, False);
/* make a delay for a short period. */
sleep(4);
/* close the connection to the X server. */
XCloseDisplay(display);
}
Simply trying to draw a moving line without flicker. I understand the concept but am a crap coder. Any help would be appreciated.
Thanks, John.

Related

Display "Hello World" on framebuffer in linux

I have used the linux 3.14 version on my ARM target and i want to show some line of characters in the display using frame buffer. I can change the colors of the display using the below code.
#include <stdio.h>
unsigned char colours[8][4] = {
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
{ 0x00, 0xFF, 0x00, 0xFF }, // green
};
int frames[] = {0,5,10,15,20,25,30};
int columns = 800;
int lines = 480;
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
int frame(int c, int l){
int i;
for(i=0; i < ARRAY_SIZE(frames); i++){
if((c==frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
return 1;
}
if((c==columns-frames[i])&&((l>=frames[i])&&l<=(lines-frames[i]))){
return 1;
}
if((l==frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
return 1;
}
if((l==lines-frames[i])&&((c>=frames[i])&&c<=(columns-frames[i]))){
return 1;
}
}
return 0;
}
int main(int argc, char **argv)
{
unsigned char pixel[3];
int l, c;
char *filename = argv[1];
printf ("Device : %s\n",filename);
FILE *f = fopen(filename,"wb");
if(f){
printf("Device open success \n");
for(l=0; l<lines; l++){
for(c=0; c < columns; c++){
if(frame(c,l)){
fwrite(colours[3], 1, sizeof(colours[3]), f);
}else{
int colour = c/(columns/ARRAY_SIZE(colours));
fwrite(colours[colour], 1, sizeof(colours[colour]), f);
}
}
}
fclose(f);
}
else
printf("Device open failed \n");
return 0;
}
In the same way i want to show some lines of character to the display. Example, I want to show characters "Hello world !!!" in the display using frame buffer.
Could any one help me to work it out.
You can find an elegant piece of code to do this in tslib. tslib is a c library for filtering touchscreen events. Actually, you don't need tslib for your purpose (yes, you don't have to build it). In their tests you can find a utility to access the framebuffer.
They have provided the fbutils.h whose implementation you can find in fbutils-linux.c. This code is very simple in that it directly manipulates the linux framebuffer and does not have any dependencies. Currently it's not even 500 lines, and if you only want to display text, you can remove other irrelevant functionality. It supports two fonts - font_8x8 and font_8x16 - whose definitions you can find in the respective .c files.
I won't go into code details as it is easy to understand. Will just list the current API and provide a simpler code for open and close functionality.
int open_framebuffer(void);
void close_framebuffer(void);
void setcolor(unsigned colidx, unsigned value);
void put_cross(int x, int y, unsigned colidx);
void put_string(int x, int y, char *s, unsigned colidx);
void put_string_center(int x, int y, char *s, unsigned colidx);
void pixel(int x, int y, unsigned colidx);
void line(int x1, int y1, int x2, int y2, unsigned colidx);
void rect(int x1, int y1, int x2, int y2, unsigned colidx);
void fillrect(int x1, int y1, int x2, int y2, unsigned colidx);
To manipulate the linux framebuffer, first you should memory map it into your process address space. After memory mapping you can access it just like an array. Using some ioctl you can get information about the framebuffer such as resolution, bytes-per-pixel etc. See here for details.
In the code below, you can pass the name of the fb device to open it, such as /dev/fb0. You can use the rest of the functions in the original code for drawing.
int open_framebuffer(const char *fbdevice)
{
uint32_t y, addr;
fb_fd = open(fbdevice, O_RDWR);
if (fb_fd == -1) {
perror("open fbdevice");
return -1;
}
if (ioctl(fb_fd, FBIOGET_FSCREENINFO, &fix) < 0) {
perror("ioctl FBIOGET_FSCREENINFO");
close(fb_fd);
return -1;
}
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, &var) < 0) {
perror("ioctl FBIOGET_VSCREENINFO");
close(fb_fd);
return -1;
}
xres_orig = var.xres;
yres_orig = var.yres;
if (rotation & 1) {
/* 1 or 3 */
y = var.yres;
yres = var.xres;
xres = y;
} else {
/* 0 or 2 */
xres = var.xres;
yres = var.yres;
}
fbuffer = mmap(NULL,
fix.smem_len,
PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED,
fb_fd,
0);
if (fbuffer == (unsigned char *)-1) {
perror("mmap framebuffer");
close(fb_fd);
return -1;
}
memset(fbuffer, 0, fix.smem_len);
bytes_per_pixel = (var.bits_per_pixel + 7) / 8;
transp_mask = ((1 << var.transp.length) - 1) <<
var.transp.offset; /* transp.length unlikely > 32 */
line_addr = malloc(sizeof(*line_addr) * var.yres_virtual);
addr = 0;
for (y = 0; y < var.yres_virtual; y++, addr += fix.line_length)
line_addr[y] = fbuffer + addr;
return 0;
}
void close_framebuffer(void)
{
memset(fbuffer, 0, fix.smem_len);
munmap(fbuffer, fix.smem_len);
close(fb_fd);
free(line_addr);
xres = 0;
yres = 0;
rotation = 0;
}
You can find examples of its usage in test programs in the folder, such as ts_test.c.
You can extend this code to support other fonts, display images etc.
Good luck!
First, I strongly suggest to avoid use of fopen/fwrite function to access devices. These function handle internal buffers that can be troublesome. Prefers functions open and write.
Next, you can't continue with series of if .. then .. else .. to render a true graphic. You need to allocate a buffer that represent your framebuffer. Its size will, be columns * lines * 4 (you need 1 byte per primary color). To write a pixel, you have to use something like:
buf[l * columns + c * 4 + 0] = red_value;
buf[l * columns + c * 4 + 1] = green_value;
buf[l * columns + c * 4 + 2] = blue_value;
buf[l * columns + c * 4 + 3] = alpha_value;
Once you buffer is fully filled, write it with:
write(fd, buf, sizeof(buf));
(where fd is file descriptor return by fd = open("/dev/fbdev0", O_WRONLY);)
Check that you are now able to set arbitrary pixels on our framebuffer.
Finally, you need a database of rendered characters. You could create it yourself, but I suggest to use https://github.com/dhepper/font8x8.
Fonts are monochrome so each bit represent one pixel. On your framebuffer, you need 4bytes for one pixel. So you will have to do some conversion.
This is a really basic way to access framebuffer, there are plenty of improvements to do:
columns, lines and pixel representation should negotiated/retrieved using FBIO*ET_*SCREENINFO ioctl.
using write to access framebuffer is not the preferred method. It is slow and does not allow to updating framebuffer easily. The preferred method use mmap.
if you want to to animate framebuffer, you to use a double buffer: allocate a buffer twice larger than necessary, write alternatively first part or second part and update shown buffer with FBIOPAN_DISPLAY
font8x8 is not ideal. You may want to use any other font available on web. You need a library to decode font format (libfreetype) and a library to render a glyph (= a letter) in a particular size to a buffer (aka rasterize step) that you can copy to your screen (libpango)
you may want to accelerate buffer copy between your glyph database and your screen framebuffer (aka compose step), but it is a far longer story that involve true GPU drivers

Shader for counting number of pixels

I'm looking for a shader CG or HLSL, that can count number of red pixels or any other colors that I want.
You could do this with atomic counters in a fragment shader. Just test the output color to see if it's within a certain tolerance of red, and if so, increment the counter. After the draw call you should be able to read the counter's value on the CPU and do whatever you like with it.
edit: added a very simple example fragment shader:
// Atomic counters require 4.2 or higher according to
// https://www.opengl.org/wiki/Atomic_Counter
#version 440
#extension GL_EXT_gpu_shader4 : enable
// Since this is a full-screen quad rendering,
// the only input we care about is texture coordinate.
in vec2 texCoord;
// Screen resolution
uniform vec2 screenRes;
// Texture info in case we use it for some reason
uniform sampler2D tex;
// Atomic counters! INCREDIBLE POWER
layout(binding = 0, offset = 0) uniform atomic_uint ac1;
// Output variable!
out vec4 colorOut;
bool isRed(vec4 c)
{
return c.r > c.g && c.r > c.b;
}
void main()
{
vec4 result = texture2D(tex, texCoord);
if (isRed(result))
{
uint cval = atomicCounterIncrement(ac1);
}
colorOut = result;
}
You would also need to set up the atomic counter in your code:
GLuint acBuffer = 0;
glGenBuffers(1, &acBuffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acBuffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);

Programmaticaly check if user is in desktop - Linux

i am making a program which needs to be run in desktop mode. How can I check in C? And also, is it possible to get screen width and height in C(of the monitor)?
This is a very broad question, but I'll bite. I'm assuming that by "desktop mode" you mean a running X window system. Since you don't seem to have a preferred widget toolkit, I'll show an example which uses Xlib.
You could simply try to open the display and check the return value. If it's up, you can retrieve the screen resolution as well:
#include <stdio.h>
#include <X11/Xlib.h>
int main(int argc, char ** argv)
{
int screen_num;
unsigned int display_width, display_height;
Display *display;
/* First connect to the display server, as specified in the DISPLAY
environment variable. */
display = XOpenDisplay(NULL);
if (!display)
{
fprintf(stderr, "unable to connect to display");
return 1;
}
/* pull useful data out of the display object */
screen_num = DefaultScreen(display);
/* Display size is a member of display structure */
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
fprintf(stdout, "resolution is %d x %d\n", display_width, display_height);
return 0;
}
You have to compile with -lX11. All of this and much more can be learned from the Xlib programming tutorial here.

cvFindContours always returns 0 - OpenCV

I'm calling the cvFindContours function inside a separate thread that I've created to handle all OpenCV work while another is kept for OpenGL stuff.
I noticed that my cvFindContours function always returns 0 when this code is executed inside a separate thread. It worked fine before, when executed in the main thread itself. I used breakpoints and Watches to evaluate value changes. everything else (variables) gets values except for contourCount (value: 0).
Any clue?
// header includes goes here
CvCapture* capture = NULL;
IplImage* frame = NULL;
IplImage* image;
IplImage* gray;
IplImage* grayContour;
CvMemStorage *storage;
CvSeq *firstcontour=NULL;
CvSeq *polycontour=NULL;
int contourCount = 0;
DWORD WINAPI startOCV(LPVOID vpParam){
capture = cvCaptureFromCAM(0); // NOTE 1
capture = cvCaptureFromCAM(0);
frame = cvQueryFrame(capture);
image = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U,3);
gray = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U,1);
grayContour = cvCreateImage(cvGetSize(image), IPL_DEPTH_8U,1);
storage = cvCreateMemStorage (0);
firstcontour=NULL;
while(1){
frame = cvQueryFrame(capture);
cvCopy(frame,image);
cvCvtColor(image,gray,CV_BGR2GRAY);
cvSmooth(gray,gray,CV_GAUSSIAN,3);
cvThreshold (gray, gray, 0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
cvNot(gray,gray);
cvCopy(gray,grayContour);
contourCount=cvFindContours (grayContour, storage, &firstcontour, sizeof (CvContour),
CV_RETR_CCOMP);
polycontour=cvApproxPoly(firstcontour,sizeof(CvContour),storagepoly,CV_POLY_APPROX_DP,3,1); // Error starts here (Pls refer to stack trace)
}
// goes on...
}
int main(int argc, char** argv){
DWORD qThreadID;
HANDLE ocvThread = CreateThread(0,0,startOCV, NULL,0, &qThreadID);
initGL(argc, argv); //some GL intitialization functions
glutMainLoop(); // draw some 3D objects
CloseHandle(ocvThread);
return 0;
}
NOTE1: these lines had to be duplicated due to the error mentioned at How to avoid "Video Source -> Capture source" selection in OpenCV 2.3.0 - Visual C++ 2008
Environment:
OpenCV 2.3.0
Visual C++ 2008
EDIT
Traces
opencv_core230d.dll!cv::error(const cv::Exception & exc={...}) Line 431 C++
opencv_imgproc230d.dll!cvPointSeqFromMat(int seq_kind=20480, const void * arr=0x00000000, CvContour * contour_header=0x01a6f514, CvSeqBlock * block=0x01a6f4f4) Line 47 + 0xbd bytes C++
opencv_imgproc230d.dll!cvApproxPoly(const void * array=0x00000000, int header_size=88, CvMemStorage * storage=0x017e7b40, int method=0, double parameter=3.0000000000000000, int parameter2=1) Line 703 + 0x28 bytes C++
Project.exe!startOCV(void * vpParam=0x00000000) Line 267 + 0x24 bytes C++
All this stuff boils down to the function CV_Assert( arr != 0 && contour_header != 0 && block != 0 ) in cvPointSeqFromMat and it fails since arr it requires is empty.
Your variable contourCount is not doing what you think it's doing. From the contours.cpp source file:
/*F///////////////////////////////////////////////////////////////////////////////////////
// Name: cvFindContours
// Purpose:
// Finds all the contours on the bi-level image.
// Context:
// Parameters:
// img - source image.
// Non-zero pixels are considered as 1-pixels
// and zero pixels as 0-pixels.
// step - full width of source image in bytes.
// size - width and height of the image in pixels
// storage - pointer to storage where will the output contours be placed.
// header_size - header size of resulting contours
// mode - mode of contour retrieval.
// method - method of approximation that is applied to contours
// first_contour - pointer to first contour pointer
// Returns:
// CV_OK or error code
// Notes:
//F*/
You are getting CV_OK == 0, which means it successfully ran. cvFindContours does not return the number of contours found to you. It merely lets you known if it failed or not. You should use the CvSeq* first_contour to figure out the number of contours detected.
Hope that helps!

GTK window motion animation?

I want to move my GTK_WINDOW across the screen automatically. Currently I have it in a draw/move loop, but that's terribly choppy. I'm very new to GTK programming (and gui programming in general). What am I missing?
You haven't said what sort of path you want the window to follow. If the path is some simple function of time -- that is, if you have a way to compute where you want the window to be at any given time -- you could try the method illustrated in following code. For the quite-simple menu in the example, it works ok on my Linux system and produces fairly smooth motion.
The key to the method is that instead of moving the window a given distance per timer event, it finds out the current time and moves the window to the location it should be at, at that time. Thus, the time derivative of speed of motion should be constant, which avoids ragged or choppy motion even if timer events occur irregularly. (As noted in g-timeout-add() description, irregularity can easily occur.)
In this example, the path is from top left of window to bottom left and back, repeatedly. The constant 'HalfTime' in timerEvent() controls how long it takes to move from corner to corner. The constant 3 in the g_timeout_add() call sets the timer interval to 0.003 seconds, or about 333 moves per second (MPS). (You may want to try more-reasonable rates, such as 20, 30, 40, etc MPS; I used the number 3 because I didn't look up g-timeout-add() before using it, and assumed the delay was hundreths of seconds, for about 33 MPS, rather than milliseconds, for about 333 MPS.) If your window contents are quite complex, fewer MPS will be practical. Also, I tried some slower rates and got more of an impression of choppiness.
/* $Id: app12.c $
Re: animating position of a top-level Gtk window
jiw July 2011 -- Offered without warranty under GPL v3
terms per http://www.gnu.org/licenses/gpl.html */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <gtk/gtk.h>
typedef struct DATA { GTimer *timer; GtkWidget *window; int w, h; }
DataStruct;
gboolean timerEvent(void *dataset) {
enum { HalfTime=8, CycTime=2*HalfTime };
gulong micros;
DataStruct *data =dataset;
double t = fabs(fmod (g_timer_elapsed (data->timer, &micros), CycTime));
int x = (t*data->w)/HalfTime, y = (t*data->h)/HalfTime;
gtk_window_move (GTK_WINDOW(data->window),
t<HalfTime? x : 2*data->w-x, t<HalfTime? y : 2*data->h-y);
return TRUE; /* Keep timeout running */
}
int main(int argc, char **argv) {
GtkWidget *vbox, *b;
GdkScreen *gds;
DataStruct data;
data.timer = g_timer_new();
gtk_init (&argc, &argv);
data.window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW(data.window), 200, 150);
g_signal_connect (G_OBJECT(data.window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
vbox = gtk_vbox_new (FALSE, 0);
gtk_container_add (GTK_CONTAINER(data.window), vbox);
b = gtk_button_new_with_label ("Click to Exit");
gtk_box_pack_start (GTK_BOX(vbox), b, TRUE, TRUE, TRUE);
g_signal_connect (b, "clicked", G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all (data.window);
gds = gdk_screen_get_default (); /* Get pointer to screen */
data.w = gdk_screen_get_width (gds); /* Find out screen width */
data.h = gdk_screen_get_height (gds); /* Find out screen height */
printf ("Screen size = %d by %d\n", data.w, data.h); fflush(stdout);
g_timeout_add(3, timerEvent, &data); /* Create .003 sec timer */
gtk_main();
return (0);
}

Resources