diff --git a/CHANGES b/CHANGES index dbe83f348..7ffcde77f 100644 --- a/CHANGES +++ b/CHANGES @@ -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 diff --git a/documentation/Fl_Menu_.html b/documentation/Fl_Menu_.html index 5aee92324..f9734599f 100644 --- a/documentation/Fl_Menu_.html +++ b/documentation/Fl_Menu_.html @@ -45,7 +45,7 @@ be "private": a dynamically allocated array managed by the Fl_Menu_.
  • copy
  • down_box
  • global
  • -
  • global
  • +
  • item_pathname
  • menu
  • @@ -146,12 +146,13 @@ that the new entry was placed at.

    FL_CTRL+'A') or a string describing the shortcut in one of two ways:

    -  [#+^]     eg. "97", "^97", "+97", "#97"
    -  [#+^]      eg. "a", "^a", "+a", "#a"
    +  [#+^]<ascii_value>    eg. "97", "^97", "+97", "#97"
    +  [#+^]<ascii_char>     eg. "a", "^a", "+a", "#a"
     
    -..where 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 <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.
       # - 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 
     global() setting (so don't destroy the widget!)

    -

    int Fl_Menu_::item_pathname(char *name, int namelen ) const;
    +

    int Fl_Menu_::item_pathname(char *name, int namelen ) const;
    int Fl_Menu_::item_pathname(char *name, int namelen, const Fl_Menu_Item *finditem) const;

    diff --git a/fluid/align_widget.cxx b/fluid/align_widget.cxx index 6f90424e5..ad5544610 100644 --- a/fluid/align_widget.cxx +++ b/fluid/align_widget.cxx @@ -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 $". // diff --git a/src/Fl_Text_Display.cxx b/src/Fl_Text_Display.cxx index 9ec01939d..623efe5ec 100644 --- a/src/Fl_Text_Display.cxx +++ b/src/Fl_Text_Display.cxx @@ -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 $". // diff --git a/src/Fl_mac.cxx b/src/Fl_mac.cxx index 2b9e528a0..b925a0008 100644 --- a/src/Fl_mac.cxx +++ b/src/Fl_mac.cxx @@ -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 // testing -#define DEBUGMSG(msg) fprintf(stderr, msg); -#define DEBUGPERRORMSG(msg) perror(msg) +#include // 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; iDataLock(); + /*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 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 $". //