Hi all,
Thx for your advices concerning my last post "Some help about
threads and Xforms and OpenGL...". Our program is now almost ready - with
various threads and forked processes, and espacially threads used to
draw in glcanvas.
However, I still have some problems in managing the Expose event
on canvas.
When a handler is added to handle expose events, the Xlib
sometimes generates GLX bad access errors ("attempt to acces private
ressource denied" - request X_GLXMakeCurrent) - and the program aborts.
It seems that handler for expose event cannot be added to a canvas
"possessed" by a thread.
Given this, I tried to superpose a FL_NO_BOX free object on the
canvas and check for expose event on it... no success, since the free
object does not receive Expose events...
I then added a prehandler on the canvas and check for FL_DRAW
events. This is, actually, my best -but still bugged - solution at that
time.
__________
You will find attached a source file with a very simple program
(should be easy to compile) which includes :
- a form
- a GLcanvas
- a thread which draws a moving cube in the canvas.
The main process creates the form and shows it ;
The thread creates the canvas and wait for drawing.
Drawing should occure in two cases : when the "go" button is
pushed (the cube is then moving) and when the canvas is Exposed.
As you will see if you run the program, everything works almost
properly. However, in some cases, the posthandler does not receive a
FL_DRAW event whereas it should.
Follow, for exemple, these steps :
1) stop moving the cube
2) bring a SMALL window above the canvas. Make sure that the
windows does not cross the border of the canvas.
3) move the window a bit.
At this step, nothing happens... The prehandler does not receive
any FL_DRAW event, the thread does not redraw the canvas, and the canvas
windows remains black where the window was...
(Note that when the window crosses the canvas border, everything
works properly... The problem only exists when the window is inside the
canvas).
I have tried several things to fix the problem. No success at all.
This is of course not very important. However, it seems to be
quite an interesting "bug" - and you may have some advices... Especially,
I don't really see why a GLXMakeCurrent should be called before an
ExposeEvent is managed within a canvas handler.
Thx,
NC
-------------
PS : I did not installed the version 1 yet, so I was unable to have a look
in the XForms sources.
PPS : it may be nice to add to the demos a program with a thread
drawing in a canvas. In case of any advice, I may fix the bug in my
"moving cube" program and send it back.
--------------------------------------------------------------------
Nicolas Castagne
Laboratoire Informatique et Creation Artistique
Association pour la Creation et la Recherche
sur les Outils d'Expression
INPG, 46 av Felix Viallet
38 000 Grenoble, France
tel : (33) 4 76 57 46 60
--------------------------------------------------------------------
- Certains hommes parlent durant leur sommeil.
- Il n'y a guere que les conferenciers pour parler
- pendant le sommeil des autres.
Alfred Camus
/*
This program implements a posix thread that draws into a glcanvas.
Compiled on SGI octane aith Xforms release 0.89
with :
cc ThreadGL.c -o ThreadGL -O2 -g -xansi -n32 -L/usr/lib32 -B static -L$HOME/XFORMS_089_N32/FORMS -lforms -B dynamic -L/usr/X11/lib -lpthread -lGL -lGLU -lXext -lX11 -lm -I$HOME/XFORMS_089_N32/FORMS -I.
*/
#include <stdio.h>
#include "forms.h"
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glx.h>
#include <pthread.h>
/* the form */
typedef struct {
FL_FORM *Form;
FL_OBJECT *GOBtn;
/* I tried to superpose a free object on the canvas
and check for expose event on it... no success...*/
/* FL_OBJECT *FreeObj; */
} TESTFORM_T;
TESTFORM_T *TestForm;
/* the canvas (created and added by the thread) */
FL_OBJECT *Canvas;
/* The threads create the canvas and perform drawings */
pthread_t DrawThread;
/* DrawOnMutex is used to stop drawing */
pthread_mutex_t DrawOnMutex = PTHREAD_MUTEX_INITIALIZER;
int ExposeEvent = 0 ;
/* ExposeEvent is used to communicate Expose events catched (or generated) by
the main process to the thread */
/* ExposeEvent = 0 => no expose event currently handled
(value 0 is set by the main process when expose is complet
ExposeEvent = 1 => expose requested
(value 1 is set by the main process)
ExposeEvent = 2 => the threads has performed the expose
(value 2 is set by the thread)
*/
/* The use of such a flag is quite a bad choice,
since both processes spin awaiting complection
of the expose event... Should be replaced with
an array of mutexes or semaphores */
/***************************************************************/
/* procedures executed only within the thread */
/***************************************************************/
void Cube(float t);
void DrawCanvas(int RotCube);
void AddCanvas();
int ExposeCanvas(int w, int h);
void * TheThread(void *data);
/***************************************************************/
/* procedures executed only within the main process */
/***************************************************************/
int Atclose(FL_FORM *Form, void *data);
/* callback for the button */
void GOBtn_cb(FL_OBJECT *ob, long data);
int CanvasPostHandler(FL_OBJECT *ob, int event,
FL_Coord mx, FL_Coord my, int key, void *raw_event);
/* int handle_free(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, */
/* int key, void *xev) */
TESTFORM_T *create_form_Form(void);
/* procedure called in case of an expose event */
/* if no thread is used : calls ExposeCancas
if a thread is used, ask the threads to call ExposeCanvas */
void RqstExpose(void);
/***************************************************************/
/* procedures executed only within the thread */
/***************************************************************/
/* draw a cube of size t */
void Cube(float t){
/* Front */
glBegin(GL_QUADS);
glNormal3f( 0, 0, 1);
glVertex3f(-t/2, t/2, t/2); glVertex3f( t/2, t/2, t/2);
glVertex3f( t/2,-t/2, t/2); glVertex3f(-t/2,-t/2, t/2);
glEnd();
/* Top */
glBegin(GL_QUADS);
glNormal3f( 0, 1, 0);
glVertex3f(-t/2, t/2, t/2); glVertex3f( t/2, t/2, t/2);
glVertex3f( t/2, t/2,-t/2); glVertex3f(-t/2, t/2,-t/2);
glEnd();
/* Right */
glBegin(GL_QUADS);
glNormal3f( 1, 0, 0);
glVertex3f( t/2, t/2, t/2); glVertex3f( t/2, t/2,-t/2);
glVertex3f( t/2,-t/2,-t/2); glVertex3f( t/2,-t/2, t/2);
glEnd();
/* Bottom */
glBegin(GL_QUADS);
glNormal3f( 0, -1, 0);
glVertex3f( t/2,-t/2, t/2); glVertex3f( t/2,-t/2,-t/2);
glVertex3f(-t/2,-t/2,-t/2); glVertex3f(-t/2,-t/2, t/2);
glEnd();
/* Left */
glBegin(GL_QUADS);
glNormal3f( -1, 0, 0);
glVertex3f(-t/2,-t/2,-t/2); glVertex3f(-t/2,-t/2, t/2);
glVertex3f(-t/2, t/2, t/2); glVertex3f(-t/2, t/2,-t/2);
glEnd();
/* Rear */
glBegin(GL_QUADS);
glNormal3f( 0, 0, -1);
glVertex3f(-t/2, t/2,-t/2); glVertex3f( t/2, t/2,-t/2);
glVertex3f( t/2,-t/2,-t/2); glVertex3f(-t/2,-t/2,-t/2);
glEnd();
}
/* draw the canvas*/
/* if RotCube == 1 : rotate the cube a bit */
/* if RotCube == 0 : redraw without rotation */
void DrawCanvas(int RotCube){
static GLfloat xRot = 0.0;
static GLfloat yRot = 0.0;
static GLfloat zRot = 0.0;
if(RotCube){
xRot+=1.0; yRot+=1.3; zRot+=2.0;
}
glClearColor(0.1, 0.1, 0.5, 0.);
glClear(GL_DEPTH_BUFFER_BIT|GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glRotatef(xRot, 1,0,0);
glRotatef(yRot, 0,1,0);
glRotatef(zRot, 0,0,1);
Cube(2);
glXSwapBuffers(fl_display, fl_get_canvas_id(Canvas));
}
/* called by the thread */
/* performs expose, given the size of the canvas */
int ExposeCanvas(int w, int h){
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, ((GLfloat)w)/(GLfloat)h, 1.0, 100.0);
gluLookAt(0,0,10,0,0,0,0,1,0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
return(1);
}
/* called by the thread */
/* adds a glcanvas to the form TestForm->Form*/
void AddCanvas(){
FL_OBJECT *obj ;
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
fl_addto_form(TestForm->Form);
Canvas = obj = fl_add_glcanvas(FL_NORMAL_CANVAS, 100, 5, 395, 390 ,"");
fl_set_object_gravity(obj, FL_NorthWest, FL_SouthEast);
/* fl_add_canvas_handler(obj, Expose, ExposeHandler, NULL);*/
/* when an handler for ExposeEvent is used */
/* the program sometimes generates Xlib sync. errors */
/* and aborts */
/* For this reason, we add a post handler on the canvas */
/* and check for FL_DRAW events */
fl_set_object_posthandler(obj, CanvasPostHandler);
fl_end_form();
}
/* the thread itself*/
void * TheThread(void *data)
{
/* The canvas must created by the thread
since some GL variables (suc as contexts)
are not shared between threads*/
AddCanvas();
while(1) {
/* should the thread draw ?*/
/* Check the mutex !*/
pthread_mutex_lock(&DrawOnMutex) ;
pthread_mutex_unlock(&DrawOnMutex);
/* in case of an Expose Event, ExposeEvent is non zero*/
if(ExposeEvent){
if(ExposeEvent == 1){
/* indicate that the ExposeEvent is gonna be performed */
ExposeEvent = 2 ;
/* perform the expose */
ExposeCanvas(Canvas->w, Canvas->h);
/* draw without moving the cube*/
DrawCanvas(0);
}
}
else {
/* move the cube and draw */
DrawCanvas(1);
}
}
}
/***************************************************************/
/* procedures executed only within the main process */
/***************************************************************/
int Atclose(FL_FORM *Form, void *data){ exit(0); return 1 ;}
TESTFORM_T *create_form_Form(void){
FL_OBJECT *obj;
TESTFORM_T *fdui = (TESTFORM_T *) fl_calloc(1, sizeof(*fdui));
fdui->Form = fl_bgn_form(FL_FLAT_BOX, 500, 400);
obj = fl_add_box(FL_FLAT_BOX,0,0,500,400 ,"");
fl_set_object_gravity(obj, FL_NorthWest, FL_SouthEast);
fdui->GOBtn = obj = fl_add_button(FL_PUSH_BUTTON,10,5,80,50,"Go !");
fl_set_object_lalign(obj, FL_ALIGN_CENTER);
fl_set_object_gravity(obj, FL_NorthWest, FL_NorthWest);
fl_set_object_callback(obj,GOBtn_cb,0);
/* fdui->FreeObj= obj = fl_add_free(FL_INACTIVE_FREE , 100, 5, 395, 390 , "", handle_free); */
/* fl_set_object_gravity(obj, FL_NorthWest, FL_SouthEast); */
/* fl_set_object_boxtype(obj, FL_NO_BOX); */
fl_end_form();
return fdui;
}
/* callback for the button */
void GOBtn_cb(FL_OBJECT *ob, long data){
if(fl_get_button(ob)){
fl_set_object_label(ob, "Stop !");
pthread_mutex_unlock(&DrawOnMutex);
}
else{
fl_set_object_label(ob, "Go !");
pthread_mutex_lock(&DrawOnMutex);
}
}
/* post handler of the canvas */
/* to check FL_DRAW event */
int CanvasPostHandler(FL_OBJECT *ob, int event, FL_Coord mx, FL_Coord my, int key, void *raw_event){
if(event == FL_DRAW)
{
printf("PostHandler Draw !\n");
RqstExpose();
}
return 0 ;
}
/* int handle_free(FL_OBJECT *obj, int event, FL_Coord mx, FL_Coord my, */
/* int key, void *xev){ */
/* printf("handle_free\n"); */
/* return !FL_PREEMPT ; */
/* } */
/* procedure called in case of an expose event */
/* asks the threads to call ExposeCanvas
and waits till completion */
void RqstExpose(void) {
ExposeEvent = 1 ;
/* allow drawing */
pthread_mutex_unlock(&DrawOnMutex);
while(ExposeEvent != 2) {
/* wait a bit to avoid wasting CPU time */
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 10000;
select(0, NULL, NULL, NULL, &sleepTime);
}
if(! fl_get_button(TestForm->GOBtn)){
pthread_mutex_lock(&DrawOnMutex);
}
ExposeEvent = 0 ;
}
/* main procedure */
int main(int argc, char *argv[]){
if(XInitThreads() == 0){
printf("************ ERROR ****************\n");
printf("XInitThreads() is not supported on this machine.\n");
printf("Abort\n");
exit(1);
}
/* create the form */
fl_initialize(&argc, argv, "Thread", 0, 0);
TestForm = create_form_Form();
fl_set_form_atclose(TestForm->Form , Atclose, 0);
fl_set_form_minsize(TestForm->Form, 200, 100);
/* show the form */
fl_show_form(TestForm->Form,FL_PLACE_POSITION,FL_FULLBORDER,argv[0]);
/* make sure the form is showned */
while(fl_check_forms());
{
pthread_attr_t t_Attr;
pthread_attr_init(&t_Attr);
/* stop drawing */
pthread_mutex_lock(&DrawOnMutex);
/* create the thread.
The thread will create the canvas, add it to the form
and wait for drawing */
pthread_create(&DrawThread, &t_Attr, TheThread, NULL);
pthread_attr_destroy(&t_Attr);
}
/* make sure the thread created and added the canvas */
{
struct timeval sleepTime;
sleepTime.tv_sec = 0;
sleepTime.tv_usec = 10000;
select(0, NULL, NULL, NULL, &sleepTime);
}
/* emulate an expose event */
RqstExpose();
while(1) fl_check_forms();
}
_________________________________________________
To unsubscribe, send the message "unsubscribe" to
xforms-request@bob.usuhs.mil or see
http://bob.usuhs.mil/mailserv/xforms.html
XForms Home Page: http://world.std.com/~xforms
List Archive: http://bob.usuhs.mil/mailserv/list-archives/
This archive was generated by hypermail 2b29 : Wed Mar 12 2003 - 16:06:43 EST