Does refresh() need to be called at least once, when using windows in ncurses? - ncurses

I've been testing ncurses, I tried doing a simple code using windows, by reading the code from the tutorials it seemed to me like calling wrefresh() was enough if changes were made just to one window. So I tried the following code, but it doesn't work, does anyone know why?
#include <ncurses.h>
int main (void) {
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
WINDOW *my_window = newwin(10, 20, 3, 4);
box(my_window, 0, 0);
wrefresh(my_window);
while ((ch=getch()) != 'q');
endwin();
return 0;
}
If I add an extra call to refresh() before wrefresh() everything works fine.
#include <ncurses.h>
int main (void) {
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
WINDOW *my_window = newwin(10, 20, 3, 4);
box(my_window, 0, 0);
refresh();
wrefresh(my_window);
while ((ch=getch()) != 'q');
endwin();
return 0;
}
I've tried several things, for instance calling refresh() after wrefresh() doesn't work either, using only refresh() also does not work. Also example 7 in this guide shows after calling refresh() once, it is enough to just call wrefresh() in the while loop.
Is it always mandatory to make a call to refresh() at least once after initscr()? the documentation does not seem to mention this.

Calling getch is the same as wgetch(stdscr). When you call wgetch, it refreshes the window that it uses as a parameter. If you are trying to refresh a different window (such as my_window), then you should use that window as the parameter.
In your example, there is nothing interesting that is written to stdscr. So you can omit the plain refresh() call.
With those changes, the program is
#include <ncurses.h>
int main (void) {
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
WINDOW *my_window = newwin(10, 20, 3, 4);
box(my_window, 0, 0);
while ((ch=wgetch(my_window)) != 'q');
endwin();
return 0;
}
For a more interesting demo, you could write into the window. That is best done with a subwindow, so that it can be scrolled, e.g.,
#include <ncurses.h>
int main (void) {
WINDOW *my_window;
WINDOW *my_scroller;
int ch;
initscr();
raw();
keypad(stdscr, TRUE);
noecho();
if ((my_window = newwin(10, 20, 3, 4)) != 0) {
box(my_window, 0, 0);
wrefresh(my_window);
if ((my_scroller = derwin(my_window, 8, 18, 1, 1)) != 0) {
scrollok(my_scroller, TRUE);
while ((ch=wgetch(my_scroller)) != 'q') {
wprintw(my_scroller, "%#x - %s\n", ch, keyname(ch));
}
}
}
endwin();
return 0;
}

Related

why the opengl can't print string

#include "stdafx.h"
#include <gl/glut.h>
void resizeEvent(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, w, h, 0);
glMatrixMode(GL_MODELVIEW);
glutPostRedisplay();
}
void displayEvent()
{
glClearColor(1, 1, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
glutSwapBuffers();
char text[] = "Hello World!";
glRasterPos2d(110, 110);
glColor3f(1, 0, 0);
for(int i=0; text[i] != '\0'; i++)
{
glutBitmapCharacter(GLUT_BITMAP_8_BY_13, text[i]);
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(400, 300);
glutInitWindowPosition(0, 0);
glutCreateWindow("Hello");
glutDisplayFunc(displayEvent);
glutReshapeFunc(resizeEvent);
glutMainLoop();
return 0;
}
I learn from a ppt,and think it will print a string.But it does not work .I search from google,but no answer. I don't know why it can't print a string.
if glutBitmapCharacter is wrong ?
All the commands in opengl, renders on the frame buffer only when flush or finish call happens, so all your commands are still in queue and yet to be displayed on frame buffer. You need to call glFlush/glFinish at the end, glut provides glutSwapBuffers, so in the end of your displayEvent function you need to call glutSwapBuffers to see the effect of your displayEvent function.

My semaphore module is not working properly(Dining philosopher)

I'm implementing a semaphore methods to understand synchronization and thread things.
By using my semaphore, I tried to solve the Dining Philosophers problem.
My plan was making deadlock situation first.
But I found that just only one philosopher eat repeatedly.
And I checked that my semaphore is working quite good by using other synchronization problems. I think there is some problem with grammar.
please let me know what is the problem.
Here is my code.
dinig.c (including main function)
#include "sem.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static tsem_t *chopstick[5];
static tsem_t *updating;
static int update_status (int i, int eating)
{
static int status[5] = { 0, };
static int duplicated;
int idx;
int sum;
tsem_wait (updating);
status[i] = eating;
/* Check invalid state. */
duplicated = 0;
sum = 0;
for (idx = 0; idx < 5; idx++)
{
sum += status[idx];
if (status[idx] && status[(idx + 1) % 5])
duplicated++;
}
/* Avoid printing empty table. */
if (sum == 0)
{
tsem_signal (updating);
return 0;
}
for (idx = 0; idx < 5; idx++)
fprintf (stdout, "%3s ", status[idx] ? "EAT" : "...");
/* Stop on invalid state. */
if (sum > 2 || duplicated > 0)
{
fprintf (stdout, "invalid %d (duplicated:%d)!\n", sum, duplicated);
exit (1);
}
else
fprintf (stdout, "\n");
tsem_signal (updating);
return 0;
}
void *thread_func (void *arg)
{
int i = (int) (long) arg;
int k = (i + 1) % 5;
do
{
tsem_wait (chopstick[i]);
tsem_wait (chopstick[k]);
update_status (i, 1);
update_status (i, 0);
tsem_signal (chopstick[i]);
tsem_signal (chopstick[k]);
}
while (1);
return NULL;
}
int main (int argc,
char **argv)
{
int i;
for (i = 0; i < 5; i++)
chopstick[i] = tsem_new (1);
updating = tsem_new (1);
for (i = 0; i < 5; i++)
{
pthread_t tid;
pthread_create (&tid, NULL, thread_func, (void *) (long) i);
}
/* endless thinking and eating... */
while (1)
usleep (10000000);
return 0;
}
sem.c(including semaphore methods)
#include "sem.h"
.
sem.h(Header for sem.c)
#ifndef __SEM_H__
#define __SEM_H__
#include <pthread.h>
typedef struct test_semaphore tsem_t;
tsem_t *tsem_new (int value);
void tsem_free (tsem_t *sem);
void tsem_wait (tsem_t *sem);
int tsem_try_wait (tsem_t *sem);
void tsem_signal (tsem_t *sem);
#endif /* __SEM_H__ */
compile command
gcc sem.c dining.c -pthread -o dining
One problem is that in tsem_wait() you have the following code sequence outside of a lock:
while(sem->count <= 0)
continue;
There's no guarantee that the program will actually re-read sem->count - the compiler is free to produce machine code that does something like the following:
int temp = sem->count;
while(temp <= 0)
continue;
In fact, this will likely happen in an optimized build.
Try changing your busy wait loop to something like this so the count is checked while holding the lock:
void tsem_wait (tsem_t *sem)
{
pthread_mutex_lock(&(sem->mutexLock));
while (sem->count <= 0) {
pthread_mutex_unlock(&(sem->mutexLock));
usleep(1);
pthread_mutex_lock(&(sem->mutexLock));
}
// sem->mutexLock is still held here...
sem->count--;
pthread_mutex_unlock(&(sem->mutexLock));
}
Strictly speaking, you should do something similar for tsem_try_wait() (which you're not using yet).
Note that you might want to consider using a pthread_cond_t to make waiting on the counter changing more efficient.
Finally, your code to 'get' the chopsticks in thread_func() has the classic Dining Philosopher deadlock problem in the situation where each philosopher simultaneously acquires the 'left' chopstick (chopstick[i]) and ends up waiting forever to get the 'right' chopstick (chopstick[k]) since all the chopsticks are in some philosopher's left hand.

How can I add new ncurses subwindows in response to user demand?

I'm trying to write an ncurses program which adds new windows in response to the user pressing keys. For example, consider the following C++ code:
#include <iostream>
#include "curses.h"
using namespace std;
int main()
{
WINDOW * win = initscr();
start_color();
noecho();
WINDOW * sub = subwin(win, 20, 20, 2, 2);
wborder(sub, 0, 0, 0, 0, 0, 0, 0, 0);
keypad(win, TRUE);
while (true)
{
int c = wgetch(win);
if (c == KEY_DOWN)
{
WINDOW* box = subwin(sub, 2, 2, (rand() % 20) + 2, (rand() % 20) + 2);
wborder(box, 0, 0, 0, 0, 0, 0, 0, 0);
}
else if (c == KEY_UP)
{
wrefresh(sub);
}
}
endwin();
return 0;
}
The user can press the down key to create new windows as many times as they want, but wrefresh will only draw them once. This appears to be related to the call to wgetch, a program that doesn't respond to keys works fine. Calling refresh also causes the problem.
The subwin man page says:
When using this routine, it is necessary to call touchwin or
touchline on orig before calling wrefresh on the subwindow.
Creating a sub-window does not actually change the parent window, so after the first refresh the parent window has not changed, touching the window marks it as changed.
So change:
wrefresh(sub);
to
touchwin(sub);
wrefresh(sub);

SDL2/SDL.h causing undefined references in Non-SDL code

I've been having an awfully strange problem that I cannot seem to grasp. I'm almost convinced that this is a compiler bug.
xTech : xIncludes.hh
#ifndef _xIncludes_
#define _xIncludes_
#define SDL_MAIN_HANDLED
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdint.h>
#include <vector>
#include <SDL2/SDL.h>
#if defined _WIN32
#include <winsock.h>
#endif
#endif
xTech : xSound.cc
#include "xSound.hh"
int xOGGStreamSource::_stream(ALuint Buffer) {
char data[BufferSize];
int size = 0;
int section;
int result;
while (size < BufferSize) {
result = ov_read(&_oggstream, data + size, BufferSize - size, 0, 2, 1, &section);
if (result > 0)
size += result;
else
if (result < 0)
return result;
else
break; //This seems a little redundant.... deal with it after it works.
}
if (size == 0) return 0;
alBufferData(Buffer, _format, data, size, _vorbisinfo->rate);
return 1;
}
void xOGGStreamSource::_empty() {
int queued;
alGetSourcei(_source, AL_BUFFERS_QUEUED, &queued);
while (queued--) {
ALuint Buffer;
alSourceUnqueueBuffers(_source, 1, &Buffer);
}
}
int xOGGStreamSource::Open(xString path) {
int result;
_oggfile = xOpenFile(path, "rb");
if (_oggfile.Buffer == NULL) {
xLogf("Audio", "Error in OGG File '%s', file does not exist.", path);
return -3;
}
if (result = ov_open(_oggfile.Buffer, &_oggstream, NULL, 0) < 0) {
xLogf("Audio", "Error in OGG File '%s', file is non-OGG.", path);
xCloseFile(_oggfile);
return -2;
}
_vorbisinfo = ov_info(&_oggstream, -1);
_vorbiscomment = ov_comment(&_oggstream, -1);
if (_vorbisinfo->channels == 1)
_format = AL_FORMAT_MONO16;
else
_format = AL_FORMAT_STEREO16;
alGenBuffers(2, _buffers);
alGenSources(1, &_source);
return 1;
}
void xOGGStreamSource::Close() {
alSourceStop(_source);
_empty();
alDeleteSources(1, &_source);
alDeleteBuffers(1, _buffers);
ov_clear(&_oggstream);
}
int xOGGStreamSource::Playback() {
if (Playing()) return 1;
if (!_stream(_buffers[0])) return 0;
if (!_stream(_buffers[1])) return 0;
alSourceQueueBuffers(_source, 2, _buffers);
alSourcePlay(_source);
return 1;
}
int xOGGStreamSource::Playing() {
ALenum state;
alGetSourcei(_source, AL_SOURCE_STATE, &state);
return (state == AL_PLAYING);
}
int xOGGStreamSource::Update(xVec3f_t Pos, xVec3f_t Vloc, xVec3f_t Dir, float Vol) {
int processed;
int active = 1;
alSource3f(_source, AL_POSITION, Pos.X, Pos.Y, Pos.Z);
alSource3f(_source, AL_VELOCITY, Vloc.X, Vloc.Y, Vloc.Z);
alSource3f(_source, AL_DIRECTION, Dir.X, Dir.Y, Dir.Z);
alSourcef (_source, AL_GAIN, Vol);
alSourcei (_source, AL_SOURCE_RELATIVE, AL_TRUE);
alGetSourcei(_source, AL_BUFFERS_PROCESSED, &processed);
while(processed--) {
ALuint Buffer;
alSourceUnqueueBuffers(_source, 1, &Buffer);
active = _stream(Buffer);
alSourceQueueBuffers(_source, 1, &Buffer);
}
return active;
}
xSound::xSound(xOGGStreamSource xss) { _source = xss; }
int xSound::PlaySound(float Volume, xVec3f_t Location) {
if (!_source.Playback()) return -3;
while(_source.Update(Location, xVec3f_t(0,0,0), xVec3f_t(0,0,0), Volume)) {
if (!_source.Playing()) {
if (!_source.Playback()) return -2;
else return -1;
}
}
_source.Close();
return 1;
}
xSoundManager::xSoundManager(){}
int xSoundManager::Init() {
_device = alcOpenDevice(NULL);
if (!_device) return -2;
_context = alcCreateContext(_device, NULL);
if (alcMakeContextCurrent(_context) == ALC_FALSE || !_context) return -1;
if (!Volume) {
xLogf("Error", "Volume in Audio is not set properly. Setting to default");
Volume = DEFAULT_VOLUME;
}
alListenerf(AL_GAIN, Volume);
if (!BufferSize) {
xLogf("Error", "Buffer size in Audio is not set properly. Setting to default");
BufferSize = DEFAULT_BUFFER_SIZE;
}
return 0;
}
xSound* xSoundManager::LoadOGG(xString file) {
xOGGStreamSource ogg;
if (ogg.Open(file) < 0) return NULL;
return new xSound(ogg);
}
xTechLibTest : main.cc
int main() {
xSetLogFile("xTechLibTest.log");
xSoundManager* audio = new xSoundManager();
if (audio->Init() < 0) return -1;
xSound* testsound1 = audio->LoadOGG("testsound.ogg");
if (testsound1 == NULL) return -2;
testsound1->PlaySound(1.0, xVec3f_t(1.0,0.5,0.3));
}
The above code and everything associated with it (string implementations, etc) work fine, no problems at all. That is until I include SDL.h; I get undefined references for every function I defined, when the compiler could find them with no problem before. It seems that the mere inclusion of SDL.h completely nullifies any definition I make. Any ideas what's going on here?
Have you properly included the linkage to the SDL libraries?
If you have built the binaries yourself, you need to include the path and library. On a linux system, if you have built the static libraries yourself, you will have a binary called libSDL2.a, however to link you need to specify SDL2 as your linked library.
Also as a side note, do you have a redundant include guard on your xsound.h file( via #ifdef _xsound_ ... ) ?
p.s. It will help the other users if you specify what how your environment is setup; compiler, system os, IDE.
It would be useful to see the output from your compiler/linker.
I've had similar problems with network related code when using Cygwin on a windows machine. I've had sockets working fine without SDL, as soon as I include SDL, the whole lot breaks with messages saying that certain header files and references can't be found.
I'm not certain, but I think it has something to do with the way that SDL has it's own main macros (here is a post about it here - simple tcp echo program not working when SDL included?).
I may be wrong, but is this similar to what you are seeing?

Get UTF-8 input with X11 Display

I've been trying and reading lots of resources on the internet, trying to find a way to get an UTF-8 keyboard (composed) input from a X Display. But I could not make it work.
I have tried the example code from this link (exaple 11-4), but no success.
I also have written a simple example (below) to try to make it work. My simple test case is to print an "é", which happens by typing the acute and then the e.
What is wrong?
Thanks,
Here is my example:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xlocale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char ** argv)
{
int screen_num, width, height;
unsigned long background, border;
Window win;
XEvent ev;
Display *dpy;
XIM im;
XIC ic;
char *failed_arg;
XIMStyles *styles;
XIMStyle xim_requested_style;
/* First connect to the display server, as specified in the DISPLAY
environment variable. */
if (setlocale(LC_ALL, "") == NULL) {
return 9;
}
if (!XSupportsLocale()) {
return 10;
}
if (XSetLocaleModifiers("") == NULL) {
return 11;
}
dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "unable to connect to display");
return 7;
}
/* these are macros that pull useful data out of the display object */
/* we use these bits of info enough to want them in their own variables */
screen_num = DefaultScreen(dpy);
background = BlackPixel(dpy, screen_num);
border = WhitePixel(dpy, screen_num);
width = 400; /* start with a small window */
height = 200;
win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), /* display, parent */
0,0, /* x, y: the window manager will place the window elsewhere */
width, height, /* width, height */
2, border, /* border width & colour, unless you have a window manager */
background); /* background colour */
/* tell the display server what kind of events we would like to see */
XSelectInput(dpy, win, ButtonPressMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask|KeymapStateMask);
/* okay, put the window on the screen, please */
XMapWindow(dpy, win);
im = XOpenIM(dpy, NULL, NULL, NULL);
if (im == NULL) {
fputs("Could not open input method\n", stdout);
return 2;
}
failed_arg = XGetIMValues(im, XNQueryInputStyle, &styles, NULL);
if (failed_arg != NULL) {
fputs("XIM Can't get styles\n", stdout);
return 3;
}
int i;
for (i = 0; i < styles->count_styles; i++) {
printf("style %d\n", styles->supported_styles[i]);
}
ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
if (ic == NULL) {
printf("Could not open IC\n");
return 4;
}
XSetICFocus(ic);
/* as each event that we asked about occurs, we respond. In this
* case we note if the window's shape changed, and exit if a button
* is pressed inside the window */
while(1) {
XNextEvent(dpy, &ev);
switch(ev.type){
case KeymapNotify:
XRefreshKeyboardMapping(&ev.xmapping);
break;
case KeyPress:
{
int count = 0;
KeySym keysym = 0;
char buf[20];
Status status = 0;
count = Xutf8LookupString(ic, (XKeyPressedEvent*)&ev, buf, 20, &keysym, &status);
printf("count: %d\n", count);
if (status==XBufferOverflow)
printf("BufferOverflow\n");
if (count)
printf("buffer: %s\n", buf);
if (status == XLookupKeySym || status == XLookupBoth) {
printf("status: %d\n", status);
}
printf("pressed KEY: %d\n", keysym);
}
break;
case KeyRelease:
{
int count = 0;
KeySym keysym = 0;
char buf[20];
Status status = 0;
count = XLookupString((XKeyEvent*)&ev, buf, 20, &keysym, NULL);
if (count)
printf("in release buffer: %s\n", buf);
printf("released KEY: %d\n", keysym);
}
break;
case ConfigureNotify:
if (width != ev.xconfigure.width
|| height != ev.xconfigure.height) {
width = ev.xconfigure.width;
height = ev.xconfigure.height;
printf("Size changed to: %d by %d", width, height);
}
break;
case ButtonPress:
XCloseDisplay(dpy);
return 0;
}
fflush(stdout);
}
}
You have to do this:
if (XFilterEvent(&ev, win))
continue;
in your event loop. This runs the input method machinery, without it you will get raw X events. For example, when you press a dead accent key followed by a letter key, and do not call XFilterEvent, you will get two KeyPress events as usual. But if you do the call, you will get three events. There are two raw events, for which XFilterEvent(&ev, win) returns True. And then there is one event synthesized by the input method, for which XFilterEvent(&ev, win) returns False. It is this third event that contains the accented character.
If you want both raw events and those synthesized by the input method, you can of course do your own raw event processing instead of continue.
Note you will need buf[count] = 0; in order to print buf correctly (or explicitly use a length), Xutf8LookupString doesn't null-terminate its output.
Finally, as mentioned in the comments, with recent versions of X11 you will need to specify a modify to XSetLocaleModifiers such as XSetLocaleModifiers("#im=none"), otherwise the extra events won't be generated.
Here is a corrected version of the code:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xlocale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char ** argv)
{
int screen_num, width, height;
unsigned long background, border;
Window win;
XEvent ev;
Display *dpy;
XIM im;
XIC ic;
char *failed_arg;
XIMStyles *styles;
XIMStyle xim_requested_style;
/* First connect to the display server, as specified in the DISPLAY
environment variable. */
if (setlocale(LC_ALL, "") == NULL) {
return 9;
}
if (!XSupportsLocale()) {
return 10;
}
if (XSetLocaleModifiers("#im=none") == NULL) {
return 11;
}
dpy = XOpenDisplay(NULL);
if (!dpy) {
fprintf(stderr, "unable to connect to display");
return 7;
}
/* these are macros that pull useful data out of the display object */
/* we use these bits of info enough to want them in their own variables */
screen_num = DefaultScreen(dpy);
background = BlackPixel(dpy, screen_num);
border = WhitePixel(dpy, screen_num);
width = 400; /* start with a small window */
height = 200;
win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), /* display, parent */
0,0, /* x, y: the window manager will place the window elsewhere */
width, height, /* width, height */
2, border, /* border width & colour, unless you have a window manager */
background); /* background colour */
/* tell the display server what kind of events we would like to see */
XSelectInput(dpy, win, ButtonPressMask|StructureNotifyMask|KeyPressMask|KeyReleaseMask);
/* okay, put the window on the screen, please */
XMapWindow(dpy, win);
im = XOpenIM(dpy, NULL, NULL, NULL);
if (im == NULL) {
fputs("Could not open input method\n", stdout);
return 2;
}
failed_arg = XGetIMValues(im, XNQueryInputStyle, &styles, NULL);
if (failed_arg != NULL) {
fputs("XIM Can't get styles\n", stdout);
return 3;
}
int i;
for (i = 0; i < styles->count_styles; i++) {
printf("style %d\n", (int)styles->supported_styles[i]);
}
ic = XCreateIC(im, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, XNClientWindow, win, NULL);
if (ic == NULL) {
printf("Could not open IC\n");
return 4;
}
XSetICFocus(ic);
/* as each event that we asked about occurs, we respond. In this
* case we note if the window's shape changed, and exit if a button
* is pressed inside the window */
while(1) {
XNextEvent(dpy, &ev);
if (XFilterEvent(&ev, win))
continue;
switch(ev.type){
case MappingNotify:
XRefreshKeyboardMapping(&ev.xmapping);
break;
case KeyPress:
{
int count = 0;
KeySym keysym = 0;
char buf[20];
Status status = 0;
count = Xutf8LookupString(ic, (XKeyPressedEvent*)&ev, buf, 20, &keysym, &status);
printf("count: %d\n", count);
if (status==XBufferOverflow)
printf("BufferOverflow\n");
if (count)
printf("buffer: %.*s\n", count, buf);
if (status == XLookupKeySym || status == XLookupBoth) {
printf("status: %d\n", status);
}
printf("pressed KEY: %d\n", (int)keysym);
}
break;
case KeyRelease:
{
int count = 0;
KeySym keysym = 0;
char buf[20];
Status status = 0;
count = XLookupString((XKeyEvent*)&ev, buf, 20, &keysym, NULL);
if (count)
printf("in release buffer: %.*s\n", count, buf);
printf("released KEY: %d\n", (int)keysym);
}
break;
case ConfigureNotify:
if (width != ev.xconfigure.width
|| height != ev.xconfigure.height) {
width = ev.xconfigure.width;
height = ev.xconfigure.height;
printf("Size changed to: %d by %d", width, height);
}
break;
case ButtonPress:
XCloseDisplay(dpy);
return 0;
}
fflush(stdout);
}
}

Resources