More doco updates.

FLUID's Layout->Center In Group functionality did not properly
handle widgets that were children of a Fl_Window widget (STR
#318)

The Fl_Text_Display destructor did not remove the predelete
callback associated with the current buffer (STR #332)

Fixed several bugs in the MacOS X Fl::add_fd() handling (STR
#333, STR #337)

The Fl_Text_Display widget did not display selections set by the
application (STR #322)


git-svn-id: file:///fltk/svn/fltk/branches/branch-1.1@3299 ea41ed52-d2ee-0310-a9c1-e6b18d33e121
This commit is contained in:
Michael R Sweet 2004-04-06 17:38:36 +00:00
parent 4e0450ea36
commit 7fdbfc61be
5 changed files with 312 additions and 249 deletions

12
CHANGES
View File

@ -1,7 +1,17 @@
CHANGES IN FLTK 1.1.5rc1
- Documentation updates (STR #245, STR #250, STR #277,
STR #281)
STR #281, STR #328, STR #338)
- FLUID's Layout->Center In Group functionality did not
properly handle widgets that were children of a
Fl_Window widget (STR #318)
- The Fl_Text_Display destructor did not remove the
predelete callback associated with the current buffer
(STR #332)
- Fixed several bugs in the MacOS X Fl::add_fd()
handling (STR #333, STR #337)
- The Fl_Text_Display widget did not display selections
set by the application (STR #322)
- FLUID crashed if you did layout with a window widget
(STR #317)
- Fl_Scroll::clear() didn't remove the child widget from

View File

@ -45,7 +45,7 @@ be "private": a dynamically allocated array managed by the Fl_Menu_.
<LI><A href=#Fl_Menu_.copy>copy</A></LI>
<LI><A href=#Fl_Menu_.down_box>down_box</A></LI>
<LI><A href=#Fl_Menu_.global>global</A></LI>
<LI><A href=#Fl_Menu_.item_pathname>global</A></LI>
<LI><A href=#Fl_Menu_.item_pathname>item_pathname</A></LI>
<LI><A href=#Fl_Menu_.menu>menu</A></LI>
</UL>
</TD><TD align=left valign=top>
@ -146,12 +146,13 @@ that the new entry was placed at.</P>
FL_CTRL+'A') or a string describing the shortcut in one of two ways:</p>
<pre>
[#+^]<ascii_value>     eg. "97", "^97", "+97", "#97"
[#+^]<ascii_char>      eg. "a", "^a", "+a", "#a"
[#+^]&lt;ascii_value&gt;  eg. "97", "^97", "+97", "#97"
[#+^]&lt;ascii_char&gt;  eg. "a", "^a", "+a", "#a"
</pre>
..where <ascii_value> is a decimal value representing an ascii character
(eg. 97 is the ascii for 'a'), and the optional prefixes enhance the value
that follows. Multiple prefixes must appear in the above order.
..where &lt;ascii_value&gt; is a decimal value representing an
ascii character (eg. 97 is the ascii for 'a'), and the optional
prefixes enhance the value that follows. Multiple prefixes must
appear in the above order.
<pre>
# - Alt
+ - Shift
@ -215,7 +216,7 @@ not have to be put in a window at all).
one will replace the old one. There is no way to remove the <TT>
global()</TT> setting (so don't destroy the widget!)</P>
<h4><a name='Fl_Menu_.item_pathnaem'>int Fl_Menu_::item_pathname(char *name, int namelen ) const;</a><br>
<h4><a name=Fl_Menu_.item_pathname>int Fl_Menu_::item_pathname(char *name, int namelen ) const;</a><br>
int Fl_Menu_::item_pathname(char *name, int namelen,
const Fl_Menu_Item *finditem) const;</h4>

View File

@ -1,5 +1,5 @@
//
// "$Id: align_widget.cxx,v 1.1.2.4 2004/04/06 02:47:25 easysw Exp $"
// "$Id: align_widget.cxx,v 1.1.2.5 2004/04/06 17:38:36 easysw Exp $"
//
// alignment code for the Fast Light Tool Kit (FLTK).
//
@ -303,7 +303,10 @@ void align_widget_cb(Fl_Widget*, long how)
{
Fl_Widget *w = ((Fl_Widget_Type *)o)->o;
Fl_Widget *p = ((Fl_Widget_Type *)o->parent)->o;
int center2 = 2*p->x()+p->w();
int center2;
if (w->window() == p) center2 = p->w();
else center2 = 2*p->x()+p->w();
w->resize((center2-w->w())/2, w->y(), w->w(), w->h());
w->redraw();
if (w->window()) w->window()->redraw();
@ -315,7 +318,10 @@ void align_widget_cb(Fl_Widget*, long how)
{
Fl_Widget *w = ((Fl_Widget_Type *)o)->o;
Fl_Widget *p = ((Fl_Widget_Type *)o->parent)->o;
int center2 = 2*p->y()+p->h();
int center2;
if (w->window() == p) center2 = p->h();
else center2 = 2*p->y()+p->h();
w->resize(w->x(), (center2-w->h())/2, w->w(), w->h());
w->redraw();
if (w->window()) w->window()->redraw();
@ -326,6 +332,6 @@ void align_widget_cb(Fl_Widget*, long how)
//
// End of "$Id: align_widget.cxx,v 1.1.2.4 2004/04/06 02:47:25 easysw Exp $".
// End of "$Id: align_widget.cxx,v 1.1.2.5 2004/04/06 17:38:36 easysw Exp $".
//

View File

@ -1,5 +1,5 @@
//
// "$Id: Fl_Text_Display.cxx,v 1.12.2.49 2004/03/01 02:05:02 easysw Exp $"
// "$Id: Fl_Text_Display.cxx,v 1.12.2.50 2004/04/06 17:38:36 easysw Exp $"
//
// Copyright 2001-2003 by Bill Spitzak and others.
// Original code Copyright Mark Edel. Permission to distribute under
@ -143,7 +143,10 @@ Fl_Text_Display::Fl_Text_Display(int X, int Y, int W, int H, const char* l)
** freed, nor are the style buffer or style table.
*/
Fl_Text_Display::~Fl_Text_Display() {
if (mBuffer) mBuffer->remove_modify_callback(buffer_modified_cb, this);
if (mBuffer) {
mBuffer->remove_modify_callback(buffer_modified_cb, this);
mBuffer->remove_predelete_callback(buffer_predelete_cb, this);
}
if (mLineStarts) delete[] mLineStarts;
}
@ -1583,13 +1586,15 @@ void Fl_Text_Display::draw_string( int style, int X, int Y, int toX,
font = styleRec->font;
fsize = styleRec->size;
if ( style & (HIGHLIGHT_MASK | PRIMARY_MASK) && Fl::focus() == this) {
background = selection_color();
if (style & (HIGHLIGHT_MASK | PRIMARY_MASK)) {
if (Fl::focus() == this) background = selection_color();
else background = fl_color_average(color(), selection_color(), 0.5f);
} else background = color();
foreground = fl_contrast(styleRec->color, background);
} else if ( style & (HIGHLIGHT_MASK | PRIMARY_MASK) && Fl::focus() == this ) {
background = selection_color();
} else if (style & (HIGHLIGHT_MASK | PRIMARY_MASK)) {
if (Fl::focus() == this) background = selection_color();
else background = fl_color_average(color(), selection_color(), 0.5f);
foreground = fl_contrast(textcolor(), background);
} else {
foreground = textcolor();
@ -3061,5 +3066,5 @@ int Fl_Text_Display::handle(int event) {
//
// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.49 2004/03/01 02:05:02 easysw Exp $".
// End of "$Id: Fl_Text_Display.cxx,v 1.12.2.50 2004/04/06 17:38:36 easysw Exp $".
//

View File

@ -1,5 +1,5 @@
//
// "$Id: Fl_mac.cxx,v 1.1.2.52 2004/03/11 05:17:12 easysw Exp $"
// "$Id: Fl_mac.cxx,v 1.1.2.53 2004/04/06 17:38:36 easysw Exp $"
//
// MacOS specific code for the Fast Light Tool Kit (FLTK).
//
@ -57,13 +57,15 @@ extern "C" {
// #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING
#ifdef DEBUG_SELECT
#include <stdio.h> // testing
#define DEBUGMSG(msg) fprintf(stderr, msg);
#define DEBUGPERRORMSG(msg) perror(msg)
#include <stdio.h> // testing
#define DEBUGMSG(msg) if ( msg ) fprintf(stderr, msg);
#define DEBUGPERRORMSG(msg) if ( msg ) perror(msg)
#define DEBUGTEXT(txt) txt
#else
#define DEBUGMSG(msg)
#define DEBUGPERRORMSG(msg)
#endif /*DEBUGSELECT*/
#define DEBUGTEXT(txt) NULL
#endif /*DEBUG_SELECT*/
// external functions
extern Fl_Window* fl_find(Window);
@ -141,127 +143,269 @@ static void nothing() {}
void (*fl_lock_function)() = nothing;
void (*fl_unlock_function)() = nothing;
//
// Select interface
//
// Select interface -- how it's implemented:
// When the user app configures one or more file descriptors to monitor
// with Fl::add_fd(), we start a separate thread to select() the data,
// sending a custom OSX 'FLTK data ready event' to the parent thread's
// RunApplicationLoop(), so that it triggers the data ready callbacks
// in the parent thread. -erco 04/04/04
//
#define POLLIN 1
#define POLLOUT 4
#define POLLERR 8
struct FD
{
int fd;
short events;
void (*cb)(int, void*);
void* arg;
};
static int nfds = 0;
static int fd_array_size = 0;
static FD *fd = 0;
static int G_pipe[2] = { 0,0 }; // work around pthread_cancel() problem
enum GetSet { GET = 1, SET = 2 };
static pthread_mutex_t select_mutex; // global data lock
// MAXFD ACCESSOR (HANDLES LOCKING)
static void MaxFD(GetSet which, int& val)
// Class to handle select() 'data ready'
class DataReady
{
static int maxfd = 0;
pthread_mutex_lock(&select_mutex);
if ( which == GET ) { val = maxfd; }
else { maxfd = val; }
pthread_mutex_unlock(&select_mutex);
struct FD
{
int fd;
short events;
void (*cb)(int, void*);
void* arg;
};
int nfds, fd_array_size;
FD *fds;
pthread_t tid; // select()'s thread id
// Data that needs to be locked (all start with '_')
pthread_mutex_t _datalock; // data lock
fd_set _fdsets[3]; // r/w/x sets user wants to monitor
int _maxfd; // max fd count to monitor
int _cancelpipe[2]; // pipe used to help cancel thread
void *_userdata; // thread's userdata
public:
DataReady()
{
nfds = 0;
fd_array_size = 0;
fds = 0;
tid = 0;
pthread_mutex_init(&_datalock, NULL);
FD_ZERO(&_fdsets[0]); FD_ZERO(&_fdsets[1]); FD_ZERO(&_fdsets[2]);
_cancelpipe[0] = _cancelpipe[1] = 0;
_userdata = 0;
_maxfd = 0;
}
~DataReady()
{
CancelThread(DEBUGTEXT("DESTRUCTOR\n"));
if (fds) { free(fds); fds = 0; }
nfds = 0;
}
// Locks
// The convention for locks: volatile vars start with '_',
// and must be locked before use. Locked code is prefixed
// with /*LOCK*/ to make painfully obvious esp. in debuggers. -erco
//
void DataLock() { pthread_mutex_lock(&_datalock); }
void DataUnlock() { pthread_mutex_unlock(&_datalock); }
// Accessors
int IsThreadRunning() { return(tid ? 1 : 0); }
int GetNfds() { return(nfds); }
int GetCancelPipe(int ix) { return(_cancelpipe[ix]); }
fd_set GetFdset(int ix) { return(_fdsets[ix]); }
// Methods
void AddFD(int n, int events, void (*cb)(int, void*), void *v);
void RemoveFD(int n, int events);
int CheckData(fd_set& r, fd_set& w, fd_set& x);
void HandleData(fd_set& r, fd_set& w, fd_set& x);
static void* DataReadyThread(void *self);
void StartThread(void *userdata);
void CancelThread(const char *reason);
};
static DataReady dataready;
void DataReady::AddFD(int n, int events, void (*cb)(int, void*), void *v)
{
RemoveFD(n, events);
int i = nfds++;
if (i >= fd_array_size)
{
FD *temp;
fd_array_size = 2*fd_array_size+1;
if (!fds) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); }
else { temp = (FD*)realloc(fds, fd_array_size*sizeof(FD)); }
if (!temp) return;
fds = temp;
}
fds[i].cb = cb;
fds[i].arg = v;
fds[i].fd = n;
fds[i].events = events;
DataLock();
/*LOCK*/ if (events & POLLIN) FD_SET(n, &_fdsets[0]);
/*LOCK*/ if (events & POLLOUT) FD_SET(n, &_fdsets[1]);
/*LOCK*/ if (events & POLLERR) FD_SET(n, &_fdsets[2]);
/*LOCK*/ if (n > _maxfd) _maxfd = n;
DataUnlock();
}
// FDSET ACCESSOR (HANDLES LOCKING)
static void Fdset(GetSet which, fd_set& r, fd_set &w, fd_set &x)
// Remove an FD from the array
void DataReady::RemoveFD(int n, int events)
{
static fd_set fdsets[3];
pthread_mutex_lock(&select_mutex);
if ( which == GET ) { r = fdsets[0]; w = fdsets[1]; x = fdsets[2]; }
else { fdsets[0] = r; fdsets[1] = w; fdsets[2] = x; }
pthread_mutex_unlock(&select_mutex);
int i,j;
for (i=j=0; i<nfds; i++)
{
if (fds[i].fd == n)
{
int e = fds[i].events & ~events;
if (!e) continue; // if no events left, delete this fd
fds[i].events = e;
}
// move it down in the array if necessary:
if (j<i)
{ fds[j] = fds[i]; }
j++;
}
nfds = j;
DataLock();
/*LOCK*/ if (events & POLLIN) FD_CLR(n, &_fdsets[0]);
/*LOCK*/ if (events & POLLOUT) FD_CLR(n, &_fdsets[1]);
/*LOCK*/ if (events & POLLERR) FD_CLR(n, &_fdsets[2]);
/*LOCK*/ if (n == _maxfd) _maxfd--;
DataUnlock();
}
// CHECK IF USER DATA READY, RETURNS r/w/x INDICATING WHICH IF ANY
int DataReady::CheckData(fd_set& r, fd_set& w, fd_set& x)
{
int ret;
DataLock();
/*LOCK*/ timeval t = { 0, 1 }; // quick check
/*LOCK*/ r = _fdsets[0], w = _fdsets[1], x = _fdsets[2];
/*LOCK*/ ret = ::select(_maxfd+1, &r, &w, &x, &t);
DataUnlock();
if ( ret == -1 )
{ DEBUGPERRORMSG("CheckData(): select()"); }
return(ret);
}
// HANDLE DATA READY CALLBACKS
void DataReady::HandleData(fd_set& r, fd_set& w, fd_set& x)
{
for (int i=0; i<nfds; i++)
{
int f = fds[i].fd;
short revents = 0;
if (FD_ISSET(f, &r)) revents |= POLLIN;
if (FD_ISSET(f, &w)) revents |= POLLOUT;
if (FD_ISSET(f, &x)) revents |= POLLERR;
if (fds[i].events & revents)
{
DEBUGMSG("DOING CALLBACK: ");
fds[i].cb(f, fds[i].arg);
DEBUGMSG("DONE\n");
}
}
}
// DATA READY THREAD
// This thread watches for changes in user's file descriptors.
// Sends a 'data ready event' to the main thread if any change.
//
void* DataReady::DataReadyThread(void *o)
{
DataReady *self = (DataReady*)o;
while ( 1 ) // loop until thread cancel or error
{
// Thread safe local copies of data before each select()
self->DataLock();
/*LOCK*/ int maxfd = self->_maxfd;
/*LOCK*/ fd_set r = self->GetFdset(0);
/*LOCK*/ fd_set w = self->GetFdset(1);
/*LOCK*/ fd_set x = self->GetFdset(2);
/*LOCK*/ void *userdata = self->_userdata;
/*LOCK*/ int cancelpipe = self->GetCancelPipe(0);
/*LOCK*/ if ( cancelpipe > maxfd ) maxfd = cancelpipe;
/*LOCK*/ FD_SET(cancelpipe, &r); // add cancelpipe to fd's to watch
/*LOCK*/ FD_SET(cancelpipe, &x);
self->DataUnlock();
// timeval t = { 1000, 0 }; // 1000 seconds;
timeval t = { 2, 0 }; // HACK: 2 secs prevents 'hanging' problem
int ret = ::select(maxfd+1, &r, &w, &x, &t);
pthread_testcancel(); // OSX 10.0.4 and older: needed for parent to cancel
switch ( ret )
{
case 0: // NO DATA
continue;
case -1: // ERROR
{
DEBUGPERRORMSG("CHILD THREAD: select() failed");
return(NULL); // error? exit thread
}
default: // DATA READY
{
if (FD_ISSET(cancelpipe, &r) || FD_ISSET(cancelpipe, &x)) // cancel?
{ return(NULL); } // just exit
DEBUGMSG("CHILD THREAD: DATA IS READY\n");
EventRef drEvent;
CreateEvent( 0, kEventClassFLTK, kEventFLTKDataReady,
0, kEventAttributeUserEvent, &drEvent);
EventQueueRef eventqueue = (EventQueueRef)userdata;
PostEventToQueue(eventqueue, drEvent, kEventPriorityStandard );
ReleaseEvent( drEvent );
return(NULL); // done with thread
}
}
}
}
// START 'DATA READY' THREAD RUNNING, CREATE INTER-THREAD PIPE
void DataReady::StartThread(void *new_userdata)
{
CancelThread(DEBUGTEXT("STARTING NEW THREAD\n"));
DataLock();
/*LOCK*/ pipe(_cancelpipe); // pipe for sending cancel msg to thread
/*LOCK*/ _userdata = new_userdata;
DataUnlock();
DEBUGMSG("*** START THREAD\n");
pthread_create(&tid, NULL, DataReadyThread, (void*)this);
}
// CANCEL 'DATA READY' THREAD, CLOSE PIPE
void DataReady::CancelThread(const char *reason)
{
if ( tid )
{
DEBUGMSG("*** CANCEL THREAD: ");
DEBUGMSG(reason);
if ( pthread_cancel(tid) == 0 ) // cancel first
{
DataLock();
/*LOCK*/ write(_cancelpipe[1], "x", 1); // wake thread from select
DataUnlock();
pthread_join(tid, NULL); // wait for thread to finish
}
tid = 0;
DEBUGMSG("(JOINED) OK\n");
}
// Close pipe if open
DataLock();
/*LOCK*/ if ( _cancelpipe[0] ) { close(_cancelpipe[0]); _cancelpipe[0] = 0; }
/*LOCK*/ if ( _cancelpipe[1] ) { close(_cancelpipe[1]); _cancelpipe[1] = 0; }
DataUnlock();
}
void Fl::add_fd( int n, int events, void (*cb)(int, void*), void *v )
{
remove_fd(n, events);
int i = nfds++;
if (i >= fd_array_size) {
FD *temp;
fd_array_size = 2*fd_array_size+1;
if (!fd) { temp = (FD*)malloc(fd_array_size*sizeof(FD)); }
else { temp = (FD*)realloc(fd, fd_array_size*sizeof(FD)); }
if (!temp) return;
fd = temp;
}
fd[i].cb = cb;
fd[i].arg = v;
fd[i].fd = n;
fd[i].events = events;
{
int maxfd;
fd_set r, w, x;
MaxFD(GET, maxfd);
Fdset(GET, r, w, x);
if (events & POLLIN) FD_SET(n, &r);
if (events & POLLOUT) FD_SET(n, &w);
if (events & POLLERR) FD_SET(n, &x);
if (n > maxfd) maxfd = n;
Fdset(SET, r, w, x);
MaxFD(SET, maxfd);
}
}
{ dataready.AddFD(n, events, cb, v); }
void Fl::add_fd(int fd, void (*cb)(int, void*), void* v)
{
Fl::add_fd(fd, POLLIN, cb, v);
}
{ dataready.AddFD(fd, POLLIN, cb, v); }
void Fl::remove_fd(int n, int events)
{
int i,j;
for (i=j=0; i<nfds; i++) {
if (fd[i].fd == n) {
int e = fd[i].events & ~events;
if (!e) continue; // if no events left, delete this fd
fd[i].events = e;
}
// move it down in the array if necessary:
if (j<i)
{ fd[j] = fd[i]; }
j++;
}
nfds = j;
{
int maxfd;
fd_set r, w, x;
MaxFD(GET, maxfd);
Fdset(GET, r, w, x);
if (events & POLLIN) FD_CLR(n, &r);
if (events & POLLOUT) FD_CLR(n, &w);
if (events & POLLERR) FD_CLR(n, &x);
if (n == maxfd) maxfd--;
Fdset(SET, r, w, x);
MaxFD(SET, maxfd);
}
}
{ dataready.RemoveFD(n, events); }
void Fl::remove_fd(int n)
{
remove_fd(n, -1);
}
{ dataready.RemoveFD(n, -1); }
/**
* Check if there is actually a message pending!
@ -272,39 +416,6 @@ int fl_ready()
return 0;
}
// CHECK IF USER DATA READY
static int CheckDataReady(fd_set& r, fd_set& w, fd_set& x)
{
int maxfd;
MaxFD(GET, maxfd);
timeval t = { 0, 1 }; // quick check
int ret = ::select(maxfd+1, &r, &w, &x, &t);
if ( ret == -1 )
{ DEBUGPERRORMSG("CheckDataReady(): select()"); }
return(ret);
}
// HANDLE DATA READY CALLBACKS
static void HandleDataReady(fd_set& r, fd_set& w, fd_set& x)
{
for (int i=0; i<nfds; i++)
{
// fprintf(stderr, "CHECKING FD %d OF %d (%d)\n", i, nfds, fd[i].fd);
int f = fd[i].fd;
short revents = 0;
if (FD_ISSET(f, &r)) revents |= POLLIN;
if (FD_ISSET(f, &w)) revents |= POLLOUT;
if (FD_ISSET(f, &x)) revents |= POLLERR;
if (fd[i].events & revents)
{
DEBUGMSG("DOING CALLBACK: ");
fd[i].cb(f, fd[i].arg);
DEBUGMSG("DONE\n");
}
}
}
/**
* handle Apple Menu items (can be created using the Fl_Sys_Menu_Bar
* returns eventNotHandledErr if the menu item could not be handled
@ -401,24 +512,21 @@ static pascal OSStatus carbonDispatchHandler( EventHandlerCallRef nextHandler, E
break;
case kEventFLTKDataReady:
{
DEBUGMSG("DATA READY EVENT: RECEIVED\n");
dataready.CancelThread(DEBUGTEXT("DATA READY EVENT\n"));
// CHILD THREAD TELLS US DATA READY
// Check to see what's ready, and invoke user's cb's
//
fd_set r, w, x;
Fdset(GET, r, w, x);
switch ( CheckDataReady(r, w, x) )
fd_set r,w,x;
switch(dataready.CheckData(r,w,x))
{
case 0: // NO DATA
break;
case -1: // ERROR
break;
default: // DATA READY
HandleDataReady(r, w, x);
break;
case 0: // NO DATA
break;
case -1: // ERROR
break;
default: // DATA READY
dataready.HandleData(r,w,x);
break;
}
}
ret = noErr;
@ -447,55 +555,6 @@ static pascal void timerProcCB( EventLoopTimerRef, void* )
fl_unlock_function();
}
// DATA READY THREAD
// Separate thread, watches for changes in user's file descriptors.
// Sends a 'data ready event' to the main thread if any change.
//
static void *dataready_thread(void *userdata)
{
EventRef drEvent;
CreateEvent( 0, kEventClassFLTK, kEventFLTKDataReady,
0, kEventAttributeUserEvent, &drEvent);
EventQueueRef eventqueue = (EventQueueRef)userdata;
// Thread safe local copy
int maxfd;
fd_set r, w, x;
MaxFD(GET, maxfd);
Fdset(GET, r, w, x);
// TACK ON FD'S FOR 'CANCEL PIPE'
FD_SET(G_pipe[0], &r);
if ( G_pipe[0] > maxfd ) maxfd = G_pipe[0];
// FOREVER UNTIL THREAD CANCEL OR ERROR
while ( 1 )
{
timeval t = { 1000, 0 }; // 1000 seconds;
int ret = ::select(maxfd+1, &r, &w, &x, &t);
pthread_testcancel(); // OSX 10.0.4 and under: need to do this
// so parent can cancel us :(
switch ( ret )
{
case 0: // NO DATA
continue;
case -1: // ERROR
{
DEBUGPERRORMSG("CHILD THREAD: select() failed");
return(NULL); // error? exit thread
}
default: // DATA READY
{
DEBUGMSG("DATA READY EVENT: SENDING\n");
PostEventToQueue(eventqueue, drEvent, kEventPriorityStandard );
return(NULL); // done with thread
}
}
}
}
/**
* break the current event loop
*/
@ -566,38 +625,20 @@ static double do_queued_events( double time = 0.0 )
got_events = 0;
// START A THREAD TO WATCH FOR DATA READY
static pthread_t dataready_tid = 0;
if ( nfds )
{
void *userdata = (void*)GetCurrentEventQueue();
// Check for re-entrant condition
if ( dataready.IsThreadRunning() )
{ dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); }
// PREPARE INTER-THREAD DATA
pthread_mutex_init(&select_mutex, NULL);
if ( G_pipe[0] ) { close(G_pipe[0]); G_pipe[0] = 0; }
if ( G_pipe[1] ) { close(G_pipe[1]); G_pipe[1] = 0; }
pipe(G_pipe);
DEBUGMSG("*** START THREAD\n");
pthread_create(&dataready_tid, NULL, dataready_thread, userdata);
}
// Start thread to watch for data ready
if ( dataready.GetNfds() )
{ dataready.StartThread((void*)GetCurrentEventQueue()); }
fl_unlock_function();
SetEventLoopTimerNextFireTime( timer, time );
RunApplicationEventLoop(); // will return after the previously set time
if ( dataready_tid != 0 )
{
DEBUGMSG("*** CANCEL THREAD: ");
pthread_cancel(dataready_tid); // cancel first
write(G_pipe[1], "x", 1); // then wakeup thread from select
pthread_join(dataready_tid, NULL); // wait for thread to finish
if ( G_pipe[0] ) { close(G_pipe[0]); G_pipe[0] = 0; }
if ( G_pipe[1] ) { close(G_pipe[1]); G_pipe[1] = 0; }
dataready_tid = 0;
DEBUGMSG("OK\n");
}
if ( dataready.IsThreadRunning() )
{ dataready.CancelThread(DEBUGTEXT("APPEVENTLOOP DONE\n")); }
fl_lock_function();
@ -1879,6 +1920,6 @@ void Fl::paste(Fl_Widget &receiver, int clipboard) {
//
// End of "$Id: Fl_mac.cxx,v 1.1.2.52 2004/03/11 05:17:12 easysw Exp $".
// End of "$Id: Fl_mac.cxx,v 1.1.2.53 2004/04/06 17:38:36 easysw Exp $".
//