From b2cffc688ea7abbddabc9ed23deea1921f59ac9d Mon Sep 17 00:00:00 2001 From: Matthias Melcher Date: Sun, 6 Dec 2009 22:21:55 +0000 Subject: Moved OS X code base to the more moder Cocoa toolkit thanks to the awesome work of Manolo Gouy (STR #2221). This is a big one! I tested all test applications under 32-bit autoconf and Xcode, and a few apps under 64bit intel. No PPC testing was done. Please verify this patch if you have the machine! git-svn-id: file:///fltk/svn/fltk/branches/branch-1.3@6951 ea41ed52-d2ee-0310-a9c1-e6b18d33e121 --- src/Fl.cxx | 52 +- src/Fl_Bitmap.cxx | 4 + src/Fl_Font.H | 3 + src/Fl_Gl_Choice.cxx | 62 +- src/Fl_Gl_Window.cxx | 29 +- src/Fl_Input.cxx | 606 ++++----- src/Fl_Sys_Menu_Bar.cxx | 193 ++- src/Fl_Window.cxx | 5 + src/Fl_Window_iconize.cxx | 4 + src/Fl_arg.cxx | 7 - src/Fl_cocoa.mm | 3105 +++++++++++++++++++++++++++++++++++++++++++++ src/Fl_compose.cxx | 12 + src/Fl_grab.cxx | 18 +- src/cgdebug.h | 1 + src/fl_arci.cxx | 12 + src/fl_ask.cxx | 3 +- src/fl_cursor.cxx | 111 +- src/fl_dnd_mac.cxx | 6 + src/fl_font_mac.cxx | 199 ++- src/fl_line_style.cxx | 11 +- src/fl_read_image_mac.cxx | 32 +- src/fl_rect.cxx | 202 ++- src/fl_scroll_area.cxx | 12 + src/fl_set_fonts_mac.cxx | 31 + src/fl_vertex.cxx | 37 +- src/gl_draw.cxx | 111 +- src/makedepend | 8 +- src/screen_xywh.cxx | 5 + 28 files changed, 4453 insertions(+), 428 deletions(-) create mode 100644 src/Fl_cocoa.mm (limited to 'src') diff --git a/src/Fl.cxx b/src/Fl.cxx index 1c39c0323..87379ed4f 100644 --- a/src/Fl.cxx +++ b/src/Fl.cxx @@ -40,6 +40,10 @@ #include #include "flstring.h" +#if defined(__APPLE__) && defined(__APPLE_COCOA__) +#import +#endif + #ifdef DEBUG # include #endif // DEBUG @@ -406,10 +410,17 @@ double Fl::wait(double time_to_wait) { // the idle function may turn off idle, we can then wait: if (idle) time_to_wait = 0.0; } +#ifdef __APPLE_COCOA__ + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#endif flush(); if (idle && !in_idle) // 'idle' may have been set within flush() time_to_wait = 0.0; - return fl_wait(time_to_wait); + double retval = fl_wait(time_to_wait); +#ifdef __APPLE_COCOA__ + [pool release]; +#endif + return retval; #else @@ -586,12 +597,13 @@ Fl_Window* fl_find(Window xid) { Fl_X *window; for (Fl_X **pp = &Fl_X::first; (window = *pp); pp = &window->next) #if defined(WIN32) || defined(USE_X11) - if (window->xid == xid) { + if (window->xid == xid) #elif defined(__APPLE_QUARTZ__) - if (window->xid == xid && !window->w->window()) { + if (window->xid == xid && !window->w->window()) #else # error unsupported platform #endif // __APPLE__ + { if (window != Fl_X::first && !Fl::modal()) { // make this window be first to speed up searches // this is not done if modal is true to avoid messing up modal stack @@ -1278,7 +1290,11 @@ int Fl_Window::handle(int ev) #if defined(USE_X11) || defined(WIN32) XMapWindow(fl_display, fl_xid(this)); // extra map calls are harmless #elif defined(__APPLE_QUARTZ__) - MacMapWindow(this, fl_xid(this)); +#ifdef __APPLE_COCOA__ + MacMapWindow(this, i->xid); +#else + MacMapWindow(this, fl_xid(this)); +#endif #else # error unsupported platform #endif // __APPLE__ @@ -1300,7 +1316,11 @@ int Fl_Window::handle(int ev) #if defined(USE_X11) || defined(WIN32) XUnmapWindow(fl_display, fl_xid(this)); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + MacUnmapWindow(this, i->xid); +#else MacUnmapWindow(this, fl_xid(this)); +#endif #else # error platform unsupported #endif @@ -1453,10 +1473,22 @@ void Fl_Widget::damage(uchar fl, int X, int Y, int W, int H) { CombineRgn(i->region, i->region, R, RGN_OR); XDestroyRegion(R); #elif defined(__APPLE_QUARTZ__) - Fl_Region R = NewRgn(); - SetRectRgn(R, X, Y, X+W, Y+H); - UnionRgn(R, i->region, i->region); - DisposeRgn(R); +#ifdef __APPLE_COCOA__ + CGRect arg = CGRectMake(X,Y,W - 1,H - 1); + int j;//don't add a rectangle totally inside the Fl_Region + for(j = 0; j < i->region->count; j++) { + if(CGRectContainsRect(i->region->rects[j], arg)) break; + } + if( j >= i->region->count) { + i->region->rects = (CGRect*)realloc(i->region->rects, (++(i->region->count)) * sizeof(CGRect)); + i->region->rects[i->region->count - 1] = arg; + } +#else + Fl_Region R = NewRgn(); + SetRectRgn(R, X, Y, X+W, Y+H); + UnionRgn(R, i->region, i->region); + DisposeRgn(R); +#endif #else # error unsupported platform #endif @@ -1480,8 +1512,12 @@ void Fl_Window::flush() { #ifdef WIN32 # include "Fl_win32.cxx" #elif defined(__APPLE__) +#ifdef __APPLE_COCOA__ +# include "Fl_cocoa.mm" +#else # include "Fl_mac.cxx" #endif +#endif // // The following methods allow callbacks to schedule the deletion of diff --git a/src/Fl_Bitmap.cxx b/src/Fl_Bitmap.cxx index 76a10a589..26019091b 100644 --- a/src/Fl_Bitmap.cxx +++ b/src/Fl_Bitmap.cxx @@ -325,7 +325,11 @@ Fl_Bitmap::~Fl_Bitmap() { void Fl_Bitmap::uncache() { if (id) { +#if defined(__APPLE__) && defined(__APPLE_COCOA__) + fl_delete_bitmask((Fl_Bitmask)id); +#else fl_delete_bitmask((Fl_Offscreen)id); +#endif id = 0; } } diff --git a/src/Fl_Font.H b/src/Fl_Font.H index 6bb2e724e..3a4408cb5 100644 --- a/src/Fl_Font.H +++ b/src/Fl_Font.H @@ -67,6 +67,9 @@ public: # elif defined(__APPLE_QUARTZ__) FL_EXPORT Fl_Font_Descriptor(const char* fontname, Fl_Fontsize size); ATSUTextLayout layout; +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + CTFontRef fontref; +#endif ATSUStyle style; short ascent, descent, q_width; // short width[256]; diff --git a/src/Fl_Gl_Choice.cxx b/src/Fl_Gl_Choice.cxx index 476bb08d8..a831eb4b6 100644 --- a/src/Fl_Gl_Choice.cxx +++ b/src/Fl_Gl_Choice.cxx @@ -295,21 +295,35 @@ GLContext fl_create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, int lay } # elif defined(__APPLE_QUARTZ__) - // warning: the Quartz version should probably use Core GL (CGL) instead of AGL - GLContext fl_create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, int layer) { - GLContext context, shared_ctx = 0; - if (context_list && nContext) shared_ctx = context_list[0]; - context = aglCreateContext( g->pixelformat, shared_ctx); - if (!context) return 0; - add_context((GLContext)context); - if ( window->parent() ) { - Rect wrect; GetWindowPortBounds( fl_xid(window), &wrect ); - GLint rect[] = { window->x(), wrect.bottom-window->h()-window->y(), window->w(), window->h() }; - aglSetInteger( (GLContext)context, AGL_BUFFER_RECT, rect ); - aglEnable( (GLContext)context, AGL_BUFFER_RECT ); - } - aglSetDrawable( context, GetWindowPort( fl_xid(window) ) ); - return (context); +// warning: the Quartz version should probably use Core GL (CGL) instead of AGL +GLContext fl_create_gl_context(Fl_Window* window, const Fl_Gl_Choice* g, int layer) { + GLContext context, shared_ctx = 0; + if (context_list && nContext) shared_ctx = context_list[0]; + context = aglCreateContext( g->pixelformat, shared_ctx); + if (!context) return 0; + add_context((GLContext)context); + if ( window->parent() ) { +#ifdef __APPLE_COCOA__ + int H = window->window()->h(); + GLint rect[] = { window->x(), H-window->h()-window->y(), window->w(), window->h() }; +#else + Rect wrect; + GetWindowPortBounds( fl_xid(window), &wrect ); + GLint rect[] = { window->x(), wrect.bottom-window->h()-window->y(), window->w(), window->h() }; +#endif + aglSetInteger( (GLContext)context, AGL_BUFFER_RECT, rect ); + aglEnable( (GLContext)context, AGL_BUFFER_RECT ); + } +#if defined(__APPLE_COCOA__) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + aglSetWindowRef(context, MACwindowRef(window) ); +#else + aglSetDrawable( context, GetWindowPort( MACwindowRef(window) ) ); +#endif +#else + aglSetDrawable( context, GetWindowPort( fl_xid(window) ) ); +#endif + return (context); } # else # error unsupported platform @@ -329,12 +343,26 @@ void fl_set_gl_context(Fl_Window* w, GLContext context) { # elif defined(__APPLE_QUARTZ__) // warning: the Quartz version should probably use Core GL (CGL) instead of AGL if ( w->parent() ) { //: resize our GL buffer rectangle - Rect wrect; GetWindowPortBounds( fl_xid(w), &wrect ); +#ifdef __APPLE_COCOA__ + int H = w->window()->h(); + GLint rect[] = { w->x(), H-w->h()-w->y(), w->w(), w->h() }; +#else + Rect wrect; + GetWindowPortBounds( fl_xid(w), &wrect ); GLint rect[] = { w->x(), wrect.bottom-w->h()-w->y(), w->w(), w->h() }; +#endif aglSetInteger( context, AGL_BUFFER_RECT, rect ); aglEnable( context, AGL_BUFFER_RECT ); } - aglSetDrawable(context, GetWindowPort( fl_xid(w) ) ); +#if defined(__APPLE_COCOA__) +#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + aglSetWindowRef(context, MACwindowRef(w) ); +#else + aglSetDrawable( context, GetWindowPort( MACwindowRef(w) ) ); +#endif +#else + aglSetDrawable( context, GetWindowPort( fl_xid(w) ) ); +#endif aglSetCurrentContext(context); # else # error unsupported platform diff --git a/src/Fl_Gl_Window.cxx b/src/Fl_Gl_Window.cxx index ae84f38de..b5b052075 100644 --- a/src/Fl_Gl_Window.cxx +++ b/src/Fl_Gl_Window.cxx @@ -66,6 +66,9 @@ int Fl_Gl_Window::can_do(int a, const int *b) { } void Fl_Gl_Window::show() { +#if defined(__APPLE__) && defined(__APPLE_COCOA__) + int need_redraw = 0; +#endif if (!shown()) { if (!g) { g = Fl_Gl_Choice::find(mode_,alist); @@ -83,12 +86,19 @@ void Fl_Gl_Window::show() { #if !defined(WIN32) && !defined(__APPLE__) Fl_X::make_xid(this, g->vis, g->colormap); if (overlay && overlay != this) ((Fl_Gl_Window*)overlay)->show(); +#elif defined(__APPLE__) && defined(__APPLE_COCOA__) + extern void MACsetContainsGLsubwindow(Fl_Window *); + if( ! parent() ) need_redraw=1; + else MACsetContainsGLsubwindow( window() ); #endif } Fl_Window::show(); #ifdef __APPLE__ set_visible(); +#ifdef __APPLE_COCOA__ + if(need_redraw) redraw();//necessary only after creation of a top-level GL window +#endif #endif /* __APPLE__ */ } @@ -154,6 +164,7 @@ int Fl_Gl_Window::mode(int m, const int *a) { being called and can also be used to implement feedback and/or selection within the handle() method. */ + void Fl_Gl_Window::make_current() { // puts("Fl_Gl_Window::make_current()"); // printf("make_current: context_=%p\n", context_); @@ -237,6 +248,15 @@ void Fl_Gl_Window::swap_buffers() { # endif #elif defined(__APPLE_QUARTZ__) // warning: the Quartz version should probably use Core GL (CGL) instead of AGL +#ifdef __APPLE_COCOA__ + if(overlay != NULL) { + //aglSwapBuffers does not work well with overlays under cocoa + glReadBuffer(GL_BACK); + glDrawBuffer(GL_FRONT); + glCopyPixels(0,0,w(),h(),GL_COLOR); + } + else +#endif aglSwapBuffers((AGLContext)context_); #else # error unsupported platform @@ -248,6 +268,7 @@ uchar fl_overlay; // changes how fl_color() works int fl_overlay_depth = 0; #endif + void Fl_Gl_Window::flush() { uchar save_valid = valid_f_ & 1; #if HAVE_GL_OVERLAY && defined(WIN32) @@ -257,13 +278,19 @@ void Fl_Gl_Window::flush() { #if defined(__APPLE_QUARTZ__) // warning: the Quartz version should probably use Core GL (CGL) instead of AGL //: clear previous clipping in this shared port +#if ! __LP64__ +#ifdef __APPLE_COCOA__ + GrafPtr port = GetWindowPort( MACwindowRef(this) ); +#else GrafPtr port = GetWindowPort( fl_xid(this) ); +#endif Rect rect; SetRect( &rect, 0, 0, 0x7fff, 0x7fff ); GrafPtr old; GetPort( &old ); SetPort( port ); ClipRect( &rect ); SetPort( old ); #endif +#endif #if HAVE_GL_OVERLAY && defined(WIN32) @@ -327,7 +354,7 @@ void Fl_Gl_Window::flush() { // don't draw if only the overlay is damaged: if (damage() != FL_DAMAGE_OVERLAY || !save_valid) draw(); - swap_buffers(); + swap_buffers(); } else { // SWAP_TYPE == UNDEFINED diff --git a/src/Fl_Input.cxx b/src/Fl_Input.cxx index 67b5f2357..92d3e1d36 100644 --- a/src/Fl_Input.cxx +++ b/src/Fl_Input.cxx @@ -82,18 +82,18 @@ static const char *legal_fp_chars = ".eE+-"; #endif int Fl_Input::handle_key() { - + char ascii = Fl::event_text()[0]; - + int repeat_num=1; - + int del; if (Fl::compose(del)) { - + // Insert characters into numeric fields after checking for legality: if (input_type() == FL_FLOAT_INPUT || input_type() == FL_INT_INPUT) { Fl::compose_reset(); // ignore any foreign letters... - + // initialize the list of legal characters inside a floating point number #ifdef HAVE_LOCALECONV if (!legal_fp_chars) { @@ -108,7 +108,7 @@ int Fl_Input::handle_key() { // the following line is not a true memory leak because the array is only // allocated once if required, and automatically freed when the program quits char *chars = (char*)malloc(len+1); - legal_fp_chars = chars; + legal_fp_chars = chars; strcpy(chars, standard_fp_chars); if (lc) { if (lc->decimal_point) strcat(chars, lc->decimal_point); @@ -118,7 +118,7 @@ int Fl_Input::handle_key() { } } #endif // HAVE_LOCALECONV - + // find the insert position int ip = position()='A'&& ascii<='F' || ascii>='a'&& ascii<='f')) || input_type()==FL_FLOAT_INPUT && ascii && strchr(legal_fp_chars, ascii)) { - if (readonly()) fl_beep(); - else replace(position(), mark(), &ascii, 1); + if (readonly()) fl_beep(); + else replace(position(), mark(), &ascii, 1); } return 1; } - + if (del || Fl::event_length()) { if (readonly()) fl_beep(); else replace(position(), del ? position()-del : mark(), - Fl::event_text(), Fl::event_length()); + Fl::event_text(), Fl::event_length()); } return 1; } - + unsigned int mods = Fl::event_state() & (FL_META|FL_CTRL|FL_ALT); switch (Fl::event_key()) { - case FL_Insert: - if (Fl::event_state() & FL_CTRL) ascii = ctrl('C'); - else if (Fl::event_state() & FL_SHIFT) ascii = ctrl('V'); - break; - case FL_Delete: + case FL_Insert: + if (Fl::event_state() & FL_CTRL) ascii = ctrl('C'); + else if (Fl::event_state() & FL_SHIFT) ascii = ctrl('V'); + break; + case FL_Delete: #ifdef __APPLE__ - if (mods==0 || mods==FL_CTRL) { // delete next char - ascii = ctrl('D'); - } else if (mods==FL_ALT) { // delete next word - if (mark() != position()) return cut(); - cut(position(), word_end(position())); - return 1; - } else if (mods==FL_META) { // delete to the end of the line - if (mark() != position()) return cut(); - cut(position(), line_end(position())); - return 1; - } else return 1; + if (mods==0 || mods==FL_CTRL) { // delete next char + ascii = ctrl('D'); + } else if (mods==FL_ALT) { // delete next word + if (mark() != position()) return cut(); + cut(position(), word_end(position())); + return 1; + } else if (mods==FL_META) { // delete to the end of the line + if (mark() != position()) return cut(); + cut(position(), line_end(position())); + return 1; + } else return 1; #else - if (mods==0) { - ascii = ctrl('D'); - } else if (mods==FL_SHIFT) { - ascii = ctrl('X'); - } else return 1; + if (mods==0) { + ascii = ctrl('D'); + } else if (mods==FL_SHIFT) { + ascii = ctrl('X'); + } else return 1; #endif - break; - case FL_Left: + break; + case FL_Left: #ifdef __APPLE__ - if (mods==0) { // char left - ascii = ctrl('B'); - } else if (mods==FL_ALT) { // word left - shift_position(word_start(position())); - return 1; - } else if (mods==FL_CTRL || mods==FL_META) { // start of line - shift_position(line_start(position())); - return 1; - } else return 1; + if (mods==0) { // char left + ascii = ctrl('B'); + } else if (mods==FL_ALT) { // word left + shift_position(word_start(position())); + return 1; + } else if (mods==FL_CTRL || mods==FL_META) { // start of line + shift_position(line_start(position())); + return 1; + } else return 1; #else - if (mods==0) { // char left - ascii = ctrl('B'); - } else if (mods==FL_CTRL) { // word left - shift_position(word_start(position())); - return 1; - } else return 1; + if (mods==0) { // char left + ascii = ctrl('B'); + } else if (mods==FL_CTRL) { // word left + shift_position(word_start(position())); + return 1; + } else return 1; #endif - break; - case FL_Right: + break; + case FL_Right: #ifdef __APPLE__ - if (mods==0) { // char right - ascii = ctrl('F'); - } else if (mods==FL_ALT) { // word right - shift_position(word_end(position())); - return 1; - } else if (mods==FL_CTRL || mods==FL_META) { // end of line - shift_position(line_end(position())); - return 1; - } else return 1; + if (mods==0) { // char right + ascii = ctrl('F'); + } else if (mods==FL_ALT) { // word right + shift_position(word_end(position())); + return 1; + } else if (mods==FL_CTRL || mods==FL_META) { // end of line + shift_position(line_end(position())); + return 1; + } else return 1; #else - if (mods==0) { // char right - ascii = ctrl('F'); - } else if (mods==FL_CTRL) { // word right - shift_position(word_end(position())); - return 1; - } else return 1; + if (mods==0) { // char right + ascii = ctrl('F'); + } else if (mods==FL_CTRL) { // word right + shift_position(word_end(position())); + return 1; + } else return 1; #endif // __APPLE__ - break; - case FL_Page_Up: + break; + case FL_Page_Up: #ifdef __APPLE__ - if (mods==0) { // scroll text one page - // OS X scrolls the view, but does not move the cursor - // Fl_Input has no scroll control, so instead we move the cursor by one page - repeat_num = linesPerPage(); - ascii = ctrl('P'); - } else if (mods==FL_ALT) { // move cursor one page - repeat_num = linesPerPage(); - ascii = ctrl('P'); - } else return 1; - break; -#else + if (mods==0) { // scroll text one page + // OS X scrolls the view, but does not move the cursor + // Fl_Input has no scroll control, so instead we move the cursor by one page + repeat_num = linesPerPage(); + ascii = ctrl('P'); + } else if (mods==FL_ALT) { // move cursor one page repeat_num = linesPerPage(); - // fall through + ascii = ctrl('P'); + } else return 1; + break; +#else + repeat_num = linesPerPage(); + // fall through #endif - case FL_Up: + case FL_Up: #ifdef __APPLE__ - if (mods==0) { // line up - ascii = ctrl('P'); - } else if (mods==FL_CTRL) { // scroll text down one page - // OS X scrolls the view, but does not move the cursor - // Fl_Input has no scroll control, so instead we move the cursor by one page - repeat_num = linesPerPage(); - ascii = ctrl('P'); - } else if (mods==FL_ALT) { // line start and up - if (line_start(position())==position() && position()>0) - return shift_position(line_start(position()-1)) + NORMAL_INPUT_MOVE; - else - return shift_position(line_start(position())) + NORMAL_INPUT_MOVE; - } else if (mods==FL_META) { // start of document - shift_position(0); - return 1; - } else return 1; + if (mods==0) { // line up + ascii = ctrl('P'); + } else if (mods==FL_CTRL) { // scroll text down one page + // OS X scrolls the view, but does not move the cursor + // Fl_Input has no scroll control, so instead we move the cursor by one page + repeat_num = linesPerPage(); + ascii = ctrl('P'); + } else if (mods==FL_ALT) { // line start and up + if (line_start(position())==position() && position()>0) + return shift_position(line_start(position()-1)) + NORMAL_INPUT_MOVE; + else + return shift_position(line_start(position())) + NORMAL_INPUT_MOVE; + } else if (mods==FL_META) { // start of document + shift_position(0); + return 1; + } else return 1; #else - if (mods==0) { // line up - ascii = ctrl('P'); - } else if (mods==FL_CTRL) { // scroll text down one line - // Fl_Input has no scroll control, so instead we move the cursor by one page - ascii = ctrl('P'); - } else return 1; + if (mods==0) { // line up + ascii = ctrl('P'); + } else if (mods==FL_CTRL) { // scroll text down one line + // Fl_Input has no scroll control, so instead we move the cursor by one page + ascii = ctrl('P'); + } else return 1; #endif - break; - case FL_Page_Down: + break; + case FL_Page_Down: #ifdef __APPLE__ - if (mods==0) { // scroll text one page - // OS X scrolls the view, but does not move the cursor - // Fl_Input has no scroll control, so instead we move the cursor by one page - repeat_num = linesPerPage(); - ascii = ctrl('N'); - } else if (mods==FL_ALT) { // move cursor one page - repeat_num = linesPerPage(); - ascii = ctrl('N'); - } else return 1; - break; -#else + if (mods==0) { // scroll text one page + // OS X scrolls the view, but does not move the cursor + // Fl_Input has no scroll control, so instead we move the cursor by one page + repeat_num = linesPerPage(); + ascii = ctrl('N'); + } else if (mods==FL_ALT) { // move cursor one page repeat_num = linesPerPage(); - // fall through + ascii = ctrl('N'); + } else return 1; + break; +#else + repeat_num = linesPerPage(); + // fall through #endif - case FL_Down: + case FL_Down: #ifdef __APPLE__ - if (mods==0) { // line down - ascii = ctrl('N'); - } else if (mods==FL_CTRL) { - // OS X scrolls the view, but does not move the cursor - // Fl_Input has no scroll control, so instead we move the cursor by one page - repeat_num = linesPerPage(); - ascii = ctrl('N'); - } else if (mods==FL_ALT) { // line end and down - if (line_end(position())==position() && position()=size()) return 0; - i = line_end(position()); - if (i == position() && i < size()) i++; - cut(position(), i); - return copy_cuts(); - case ctrl('N'): // go down one line - i = position(); - if (line_end(i) >= size()) return NORMAL_INPUT_MOVE; - while (repeat_num--) { - i = line_end(i); - if (i >= size()) break; - i++; - } - shift_up_down_position(i); + } + if (mark() != position()) return cut(); + else return cut(1); + case ctrl('E'): // go to the end of the line + return shift_position(line_end(position())) + NORMAL_INPUT_MOVE; + case ctrl('F'): // go to the next character + return shift_position(position()+1) + NORMAL_INPUT_MOVE; + case ctrl('H'): // cut the previous character + if (readonly()) { + fl_beep(); return 1; - case ctrl('P'): // go up one line - i = position(); - if (!line_start(i)) return NORMAL_INPUT_MOVE; - while(repeat_num--) { - i = line_start(i); - if (!i) break; - i--; - } - shift_up_down_position(line_start(i)); + } + if (mark() != position()) cut(); + else cut(-1); + return 1; + case ctrl('K'): // cut to the end of the line + if (readonly()) { + fl_beep(); return 1; - case ctrl('U'): // clear the whole document? - if (readonly()) { - fl_beep(); - return 1; - } - return cut(0, size()); - case ctrl('V'): // paste text - case ctrl('Y'): - if (readonly()) { - fl_beep(); - return 1; - } - Fl::paste(*this, 1); + } + if (position()>=size()) return 0; + i = line_end(position()); + if (i == position() && i < size()) i++; + cut(position(), i); + return copy_cuts(); + case ctrl('N'): // go down one line + i = position(); + if (line_end(i) >= size()) return NORMAL_INPUT_MOVE; + while (repeat_num--) { + i = line_end(i); + if (i >= size()) break; + i++; + } + shift_up_down_position(i); + return 1; + case ctrl('P'): // go up one line + i = position(); + if (!line_start(i)) return NORMAL_INPUT_MOVE; + while(repeat_num--) { + i = line_start(i); + if (!i) break; + i--; + } + shift_up_down_position(line_start(i)); + return 1; + case ctrl('U'): // clear the whole document? + if (readonly()) { + fl_beep(); return 1; - case ctrl('X'): // cut the selected text - case ctrl('W'): - if (readonly()) { - fl_beep(); - return 1; - } - copy(1); - return cut(); - case ctrl('Z'): // undo - case ctrl('_'): - if (readonly()) { - fl_beep(); - return 1; - } - return undo(); - case ctrl('I'): // insert literal - case ctrl('J'): - case ctrl('L'): - case ctrl('M'): - if (readonly()) { - fl_beep(); - return 1; - } - // insert a few selected control characters literally: - if (input_type() != FL_FLOAT_INPUT && input_type() != FL_INT_INPUT) - return replace(position(), mark(), &ascii, 1); + } + return cut(0, size()); + case ctrl('V'): // paste text + case ctrl('Y'): + if (readonly()) { + fl_beep(); + return 1; + } + Fl::paste(*this, 1); + return 1; + case ctrl('X'): // cut the selected text + case ctrl('W'): + if (readonly()) { + fl_beep(); + return 1; + } + copy(1); + return cut(); + case ctrl('Z'): // undo + case ctrl('_'): + if (readonly()) { + fl_beep(); + return 1; + } + return undo(); + case ctrl('I'): // insert literal + case ctrl('J'): + case ctrl('L'): + case ctrl('M'): + if (readonly()) { + fl_beep(); + return 1; + } + // insert a few selected control characters literally: + if (input_type() != FL_FLOAT_INPUT && input_type() != FL_INT_INPUT) + return replace(position(), mark(), &ascii, 1); } - + return 0; } diff --git a/src/Fl_Sys_Menu_Bar.cxx b/src/Fl_Sys_Menu_Bar.cxx index 6092f75bb..da0451582 100644 --- a/src/Fl_Sys_Menu_Bar.cxx +++ b/src/Fl_Sys_Menu_Bar.cxx @@ -61,6 +61,12 @@ #include "flstring.h" #include #include +#include + +#ifdef __APPLE_COCOA__ +extern void *MACMenuOrItemOperation(const char *operation, ...); +#define MenuHandle void * +#endif typedef const Fl_Menu_Item *pFl_Menu_Item; @@ -69,7 +75,9 @@ typedef const Fl_Menu_Item *pFl_Menu_Item; * Skip all '&' which would mark the shortcut in FLTK * Skip all Mac control characters ('(', '<', ';', '^', '!' ) */ -static void catMenuText( const char *src, char *dst ) + +#ifndef __APPLE_COCOA__ + static void catMenuText( const char *src, char *dst ) { char c; while ( *dst ) @@ -88,6 +96,7 @@ static void catMenuText( const char *src, char *dst ) * append a marker to identify the menu font style * labeltype_ && !m->labelfont_ ) @@ -110,11 +119,11 @@ static void catMenuFont( const Fl_Menu_Item *m, char *dst ) strcat( dst, "labeltype_ == FL_SYMBOL_LABEL ) ; // not supported -} +} /** * append a marker to identify the menu shortcut - * shortcut_ ) @@ -135,6 +146,11 @@ static void setMenuShortcut( MenuHandle mh, int miCnt, const Fl_Menu_Item *m ) if ( !isalnum( key ) ) return; +#ifdef __APPLE_COCOA__ + void *menuItem = MACMenuOrItemOperation("itemAtIndex", mh, miCnt); + MACMenuOrItemOperation("setKeyEquivalent", menuItem, key ); + MACMenuOrItemOperation("setKeyEquivalentModifierMask", menuItem, m->shortcut_ ); +#else long macMod = kMenuNoCommandModifier; if ( m->shortcut_ & FL_META ) macMod = kMenuNoModifiers; if ( m->shortcut_ & FL_SHIFT || isupper(key) ) macMod |= kMenuShiftModifier; @@ -144,8 +160,10 @@ static void setMenuShortcut( MenuHandle mh, int miCnt, const Fl_Menu_Item *m ) //SetMenuItemKeyGlyph( mh, miCnt, key ); SetItemCmd( mh, miCnt, toupper(key) ); SetMenuItemModifiers( mh, miCnt, macMod ); +#endif } + #if 0 // this function needs to be verified before we compile it back in. static void catMenuShortcut( const Fl_Menu_Item *m, char *dst ) @@ -166,16 +184,113 @@ static void catMenuShortcut( const Fl_Menu_Item *m, char *dst ) } #endif + static void setMenuFlags( MenuHandle mh, int miCnt, const Fl_Menu_Item *m ) { if ( m->flags & FL_MENU_TOGGLE ) { - SetItemMark( mh, miCnt, ( m->flags & FL_MENU_VALUE ) ? 0x12 : 0 ); +#ifdef __APPLE_COCOA__ + void *menuItem = MACMenuOrItemOperation("itemAtIndex", mh, miCnt); + MACMenuOrItemOperation("setState", menuItem, m->flags & FL_MENU_VALUE ); +#else + SetItemMark( mh, miCnt, ( m->flags & FL_MENU_VALUE ) ? 0x12 : 0 ); +#endif + } + else if ( m->flags & FL_MENU_RADIO ) { +#ifndef __APPLE_COCOA__ + SetItemMark( mh, miCnt, ( m->flags & FL_MENU_VALUE ) ? 0x13 : 0 ); +#endif } - else if ( m->flags & FL_MENU_RADIO ) - SetItemMark( mh, miCnt, ( m->flags & FL_MENU_VALUE ) ? 0x13 : 0 ); } + +#ifdef __APPLE_COCOA__ + +/** + * create a sub menu for a specific menu handle + */ +static void createSubMenu( void * mh, pFl_Menu_Item &mm ) +{ + void *submenu; + int miCnt, flags; + + void *menuItem; + submenu = MACMenuOrItemOperation("initWithTitle", mm->text); + int cnt; + MACMenuOrItemOperation("numberOfItems", mh, &cnt); + cnt--; + menuItem = MACMenuOrItemOperation("itemAtIndex", mh, cnt); + MACMenuOrItemOperation("setSubmenu", menuItem, submenu); + if ( mm->flags & FL_MENU_INACTIVE ) { + MACMenuOrItemOperation("setEnabled", menuItem, 0); + } + mm++; + + while ( mm->text ) + { + MACMenuOrItemOperation("addNewItem", submenu, mm->text, mm->callback_, mm->user_data_, &miCnt); + setMenuFlags( submenu, miCnt, mm ); + setMenuShortcut( submenu, miCnt, mm ); + if ( mm->flags & FL_MENU_INACTIVE ) { + void *item = MACMenuOrItemOperation("itemAtIndex", submenu, miCnt); + MACMenuOrItemOperation("setEnabled", item, 0); + } + flags = mm->flags; + if ( mm->flags & FL_SUBMENU ) + { + createSubMenu( submenu, mm ); + } + else if ( mm->flags & FL_SUBMENU_POINTER ) + { + const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_; + createSubMenu( submenu, smm ); + } + if ( flags & FL_MENU_DIVIDER ) { + MACMenuOrItemOperation("addSeparatorItem", submenu); + } + mm++; + } +} + + +/** + * create a system menu bar using the given list of menu structs + * + * \author Matthias Melcher + * + * @param m list of Fl_Menu_Item + */ +extern void *MACmainMenu(void); +void Fl_Sys_Menu_Bar::menu(const Fl_Menu_Item *m) +{ + fl_open_display(); + Fl_Menu_Bar::menu( m ); + fl_sys_menu_bar = this; + + + const Fl_Menu_Item *mm = m; + for (;;) + { + if ( !mm || !mm->text ) + break; + char visible = mm->visible() ? 1 : 0; + MACMenuOrItemOperation("addNewItem", MACmainMenu(), mm->text, NULL, NULL, NULL); + + if ( mm->flags & FL_SUBMENU ) + createSubMenu( MACmainMenu(), mm ); + else if ( mm->flags & FL_SUBMENU_POINTER ) { + const Fl_Menu_Item *smm = (Fl_Menu_Item*)mm->user_data_; + createSubMenu( MACmainMenu(), smm ); + } + if ( visible ) { +// InsertMenu( mh, 0 ); + } + mm++; + } +} + +#else + static void catMenuFlags( const Fl_Menu_Item *m, char *dst ) { if ( !m->flags ) @@ -184,6 +299,7 @@ static void catMenuFlags( const Fl_Menu_Item *m, char *dst ) strcat( dst, "(" ); } + /** * create a sub menu for a specific menu handle */ @@ -229,7 +345,7 @@ static void createSubMenu( MenuHandle mh, int &cnt, pFl_Menu_Item &mm ) } InsertMenu( mh, -1 ); } - + /** * create a system menu bar using the given list of menu structs @@ -243,9 +359,9 @@ void Fl_Sys_Menu_Bar::menu(const Fl_Menu_Item *m) fl_open_display(); Fl_Menu_Bar::menu( m ); fl_sys_menu_bar = this; - + char buf[255]; - + int cnt = 1; // first menu is no 2. no 1 is the Apple Menu const Fl_Menu_Item *mm = m; for (;;) @@ -276,6 +392,8 @@ void Fl_Sys_Menu_Bar::menu(const Fl_Menu_Item *m) DrawMenuBar(); } +#endif //__APPLE_COCOA__ + /* const Fl_Menu_Item* Fl_Sys_Menu_Bar::picked(const Fl_Menu_Item* v) { Fl_menu_Item *ret = Fl_Menu_Bar::picked( v ); @@ -290,42 +408,31 @@ const Fl_Menu_Item* Fl_Sys_Menu_Bar::picked(const Fl_Menu_Item* v) { */ void Fl_Sys_Menu_Bar::draw() { -/* -- nothing to do, system should take care of this - draw_box(); - if (!menu() || !menu()->text) return; - const Fl_Menu_Item* m; - int X = x()+6; - for (m=menu(); m->text; m = m->next()) { - int W = m->measure(0,this) + 16; - m->draw(X, y(), W, h(), this); - X += W; - } - */ } /* -int Fl_Menu_Bar::handle(int event) { - const Fl_Menu_Item* v; - if (menu() && menu()->text) switch (event) { - case FL_ENTER: - case FL_LEAVE: - return 1; - case FL_PUSH: - v = 0; - J1: - v = menu()->pulldown(x(), y(), w(), h(), v, this, 0, 1); - picked(v); - return 1; - case FL_SHORTCUT: - if (visible_r()) { - v = menu()->find_shortcut(); - if (v && v->submenu()) goto J1; - } - return test_shortcut() != 0; - } - return 0; -} -*/ + int Fl_Menu_Bar::handle(int event) { + const Fl_Menu_Item* v; + if (menu() && menu()->text) switch (event) { + case FL_ENTER: + case FL_LEAVE: + return 1; + case FL_PUSH: + v = 0; + J1: + v = menu()->pulldown(x(), y(), w(), h(), v, this, 0, 1); + picked(v); + return 1; + case FL_SHORTCUT: + if (visible_r()) { + v = menu()->find_shortcut(); + if (v && v->submenu()) goto J1; + } + return test_shortcut() != 0; + } + return 0; + } + */ #endif /* __APPLE__ */ diff --git a/src/Fl_Window.cxx b/src/Fl_Window.cxx index 4f54e7ebc..692aa255e 100644 --- a/src/Fl_Window.cxx +++ b/src/Fl_Window.cxx @@ -31,6 +31,7 @@ // equivalent (but totally different) crap for MSWindows is in Fl_win32.cxx #include "config.h" #include +#include #include #include #include "flstring.h" @@ -125,6 +126,10 @@ void Fl_Window::draw() { int i; for (i=dx; i<12; i++) { fl_color(c[i&3]); +#ifdef __APPLE_COCOA__ + extern CGContextRef fl_gc; + if(fl_gc) +#endif fl_line(x1--, y1, x2, y2--); } } diff --git a/src/Fl_Window_iconize.cxx b/src/Fl_Window_iconize.cxx index 83a1b810c..2341a95fe 100644 --- a/src/Fl_Window_iconize.cxx +++ b/src/Fl_Window_iconize.cxx @@ -37,7 +37,11 @@ void Fl_Window::iconize() { #ifdef WIN32 ShowWindow(i->xid, SW_SHOWMINNOACTIVE); #elif defined(__APPLE__) +#ifdef __APPLE_COCOA__ + MacCollapseWindow((Window)i->xid); +#else CollapseWindow( i->xid, true ); +#endif #else XIconifyWindow(fl_display, i->xid, fl_screen); #endif diff --git a/src/Fl_arg.cxx b/src/Fl_arg.cxx index 253857a1c..b1476faa9 100644 --- a/src/Fl_arg.cxx +++ b/src/Fl_arg.cxx @@ -88,13 +88,6 @@ int Fl::arg(int argc, char **argv, int &i) { if (s[0] != '-' || s[1] == '-' || !s[1]) {return_i = 1; return 0;} s++; // point after the dash -#ifdef __APPLE__ - if (!strncmp(s, "psn", 3)) { - // Skip process serial number... - i++; - } - else -#endif // __APPLE__ if (fl_match(s, "iconic")) { fl_show_iconic = 1; i++; diff --git a/src/Fl_cocoa.mm b/src/Fl_cocoa.mm new file mode 100644 index 000000000..e20b5c76d --- /dev/null +++ b/src/Fl_cocoa.mm @@ -0,0 +1,3105 @@ +// +// "$Id: Fl_mac.cxx 6758 2009-04-13 07:32:01Z matt $" +// +// MacOS specific code for the Fast Light Tool Kit (FLTK). +// +// Copyright 1998-2009 by Bill Spitzak and others. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library General Public +// License as published by the Free Software Foundation; either +// version 2 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Library General Public License for more details. +// +// You should have received a copy of the GNU Library General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +// USA. +// +// Please report all bugs and problems on the following page: +// +// http://www.fltk.org/str.php +// + +//// From the inner edge of a MetroWerks CodeWarrior CD: +// (without permission) +// +// "Three Compiles for 68Ks under the sky, +// Seven Compiles for PPCs in their fragments of code, +// Nine Compiles for Mortal Carbon doomed to die, +// One Compile for Mach-O Cocoa on its Mach-O throne, +// in the Land of MacOS X where the Drop-Shadows lie. +// +// One Compile to link them all, One Compile to merge them, +// One Compile to copy them all and in the bundle bind them, +// in the Land of MacOS X where the Drop-Shadows lie." + +// we don't need the following definition because we deliver only +// true mouse moves. On very slow systems however, this flag may +// still be useful. +#ifndef FL_DOXYGEN + +#define CONSOLIDATE_MOTION 0 +extern "C" { +#include +} + + +#include +#include +#include +#include +#include +#include +#include +#include "flstring.h" +#include +#include + +#import +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 +typedef long NSInteger; +typedef unsigned long NSUInteger; +#endif + + +// #define DEBUG_SELECT // UNCOMMENT FOR SELECT()/THREAD DEBUGGING +#ifdef DEBUG_SELECT +#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) +#define DEBUGTEXT(txt) NULL +#endif /*DEBUG_SELECT*/ + +// external functions +extern void fl_fix_focus(); +extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h); +extern CGContextRef CreateWatchImage(void); +extern CGContextRef CreateHelpImage(void); +extern CGContextRef CreateNESWImage(void); +extern CGContextRef CreateNWSEImage(void); +extern CGContextRef CreateNoneImage(void); + +// forward definition of functions in this file +// converting cr lf converter function +static void convert_crlf(char * string, size_t len); +static void createAppleMenu(void); +Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h); +static void cocoaMouseHandler(NSEvent *theEvent); + +// public variables +int fl_screen; +CGContextRef fl_gc = 0; +void *fl_system_menu; // this is really a NSMenu* +Fl_Sys_Menu_Bar *fl_sys_menu_bar = 0; +void *fl_default_cursor; // this is really a NSCursor* +void *fl_capture = 0; // (NSWindow*) we need this to compensate for a missing(?) mouse capture +//ulong fl_event_time; // the last timestamp from an x event +char fl_key_vector[32]; // used by Fl::get_key() +bool fl_show_iconic; // true if called from iconize() - shows the next created window in collapsed state +int fl_disable_transient_for; // secret method of removing TRANSIENT_FOR +//const Fl_Window* fl_modal_for; // parent of modal() window +Fl_Region fl_window_region = 0; +Window fl_window; +Fl_Window *Fl_Window::current_; +//EventRef fl_os_event; // last (mouse) event + +// forward declarations of variables in this file +static int got_events = 0; +static Fl_Window* resize_from_system; +static NSView *viewWithLockedFocus = nil; +static SInt32 MACsystemVersion; + +#if CONSOLIDATE_MOTION +static Fl_Window* send_motion; +extern Fl_Window* fl_xmousewin; +#endif + +enum { FLTKTimerEvent = 1, FLTKDataReadyEvent }; + + +/* fltk-utf8 placekeepers */ +void fl_reset_spot() +{ +} + +void fl_set_spot(int font, int size, int X, int Y, int W, int H, Fl_Window *win) +{ +} + +void fl_set_status(int x, int y, int w, int h) +{ +} + +/** + * Mac keyboard lookup table + */ +static unsigned short macKeyLookUp[128] = +{ + 'a', 's', 'd', 'f', 'h', 'g', 'z', 'x', + 'c', 'v', '^', 'b', 'q', 'w', 'e', 'r', + + 'y', 't', '1', '2', '3', '4', '6', '5', + '=', '9', '7', '-', '8', '0', ']', 'o', + + 'u', '[', 'i', 'p', FL_Enter, 'l', 'j', '\'', + 'k', ';', '\\', ',', '/', 'n', 'm', '.', + + FL_Tab, ' ', '`', FL_BackSpace, + FL_KP_Enter, FL_Escape, 0, 0/*FL_Meta_L*/, + 0/*FL_Shift_L*/, 0/*FL_Caps_Lock*/, 0/*FL_Alt_L*/, 0/*FL_Control_L*/, + 0/*FL_Shift_R*/, 0/*FL_Alt_R*/, 0/*FL_Control_R*/, 0, + + 0, FL_KP+'.', FL_Right, FL_KP+'*', 0, FL_KP+'+', FL_Left, FL_Delete, + FL_Down, 0, 0, FL_KP+'/', FL_KP_Enter, FL_Up, FL_KP+'-', 0, + + 0, FL_KP+'=', FL_KP+'0', FL_KP+'1', FL_KP+'2', FL_KP+'3', FL_KP+'4', FL_KP+'5', + FL_KP+'6', FL_KP+'7', 0, FL_KP+'8', FL_KP+'9', 0, 0, 0, + + FL_F+5, FL_F+6, FL_F+7, FL_F+3, FL_F+8, FL_F+9, 0, FL_F+11, + 0, 0/*FL_F+13*/, FL_Print, FL_Scroll_Lock, 0, FL_F+10, FL_Menu, FL_F+12, + + 0, FL_Pause, FL_Help, FL_Home, FL_Page_Up, FL_Delete, FL_F+4, FL_End, + FL_F+2, FL_Page_Down, FL_F+1, FL_Left, FL_Right, FL_Down, FL_Up, 0/*FL_Power*/, +}; + +/** + * convert the current mouse chord into the FLTK modifier state + */ +static unsigned int mods_to_e_state( NSUInteger mods ) +{ + long state = 0; + if ( mods & NSNumericPadKeyMask ) state |= FL_NUM_LOCK; + if ( mods & NSCommandKeyMask ) state |= FL_META; + if ( mods & NSAlternateKeyMask ) state |= FL_ALT; + if ( mods & NSControlKeyMask ) state |= FL_CTRL; + if ( mods & NSShiftKeyMask ) state |= FL_SHIFT; + if ( mods & NSAlphaShiftKeyMask ) state |= FL_CAPS_LOCK; + unsigned int ret = ( Fl::e_state & 0xff000000 ) | state; + Fl::e_state = ret; + //printf( "State 0x%08x (%04x)\n", Fl::e_state, mods ); + return ret; +} + + +/** + * convert the current mouse chord into the FLTK keysym + */ +/* + static void mods_to_e_keysym( NSUInteger mods ) + { + if ( mods & NSCommandKeyMask ) Fl::e_keysym = FL_Meta_L; + else if ( mods & NSNumericPadKeyMask ) Fl::e_keysym = FL_Num_Lock; + else if ( mods & NSAlternateKeyMask ) Fl::e_keysym = FL_Alt_L; + else if ( mods & NSControlKeyMask ) Fl::e_keysym = FL_Control_L; + else if ( mods & NSShiftKeyMask ) Fl::e_keysym = FL_Shift_L; + else if ( mods & NSAlphaShiftKeyMask ) Fl::e_keysym = FL_Caps_Lock; + else Fl::e_keysym = 0; + //printf( "to sym 0x%08x (%04x)\n", Fl::e_keysym, mods ); + }*/ +// these pointers are set by the Fl::lock() function: +static void nothing() {} +void (*fl_lock_function)() = nothing; +void (*fl_unlock_function)() = nothing; + +// +// 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 + +// Class to handle select() 'data ready' +class DataReady +{ + 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(); +} + +// Remove an FD from the array +void DataReady::RemoveFD(int n, int events) +{ + 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"); + NSPoint pt={0,0}; + NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt modifierFlags:0 + timestamp:0 + windowNumber:0 context:NULL subtype:FLTKDataReadyEvent data1:0 data2:0]; + [NSApp postEvent:event atStart:NO]; + 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 ) +{ dataready.AddFD(n, events, cb, v); } + +void Fl::add_fd(int fd, void (*cb)(int, void*), void* v) +{ dataready.AddFD(fd, POLLIN, cb, v); } + +void Fl::remove_fd(int n, int events) +{ dataready.RemoveFD(n, events); } + +void Fl::remove_fd(int n) +{ dataready.RemoveFD(n, -1); } + +/** + * Check if there is actually a message pending! + */ +int fl_ready() +{ + NSEvent *retval = [NSApp nextEventMatchingMask:NSAnyEventMask untilDate:[NSDate dateWithTimeIntervalSinceNow:0] + inMode:NSDefaultRunLoopMode dequeue:NO]; + if(retval != nil) [retval release]; + return retval != nil; +} + + +static void processFLTKEvent(void) { + 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; + switch(dataready.CheckData(r,w,x)) + { + case 0: // NO DATA + break; + case -1: // ERROR + break; + default: // DATA READY + dataready.HandleData(r,w,x); + break; + } + return; +} + + +/** + * break the current event loop + */ +static void breakMacEventLoop() +{ + fl_lock_function(); + + NSPoint pt={0,0}; + NSEvent *event = [NSEvent otherEventWithType:NSApplicationDefined location:pt modifierFlags:0 + timestamp:0 + windowNumber:0 context:NULL subtype:FLTKTimerEvent data1:0 data2:0]; + [NSApp postEvent:event atStart:NO]; + fl_unlock_function(); +} + +// +// MacOS X timers +// + +struct MacTimeout { + Fl_Timeout_Handler callback; + void* data; + EventLoopTimerRef timer; + EventLoopTimerUPP upp; + char pending; +}; +static MacTimeout* mac_timers; +static int mac_timer_alloc; +static int mac_timer_used; + + +static void realloc_timers() +{ + if (mac_timer_alloc == 0) { + mac_timer_alloc = 8; + } + mac_timer_alloc *= 2; + MacTimeout* new_timers = new MacTimeout[mac_timer_alloc]; + memset(new_timers, 0, sizeof(MacTimeout)*mac_timer_alloc); + memcpy(new_timers, mac_timers, sizeof(MacTimeout) * mac_timer_used); + MacTimeout* delete_me = mac_timers; + mac_timers = new_timers; + delete [] delete_me; +} + +static void delete_timer(MacTimeout& t) +{ + if (t.timer) { + RemoveEventLoopTimer(t.timer); + DisposeEventLoopTimerUPP(t.upp); + memset(&t, 0, sizeof(MacTimeout)); + } +} + + +static void do_timer(EventLoopTimerRef timer, void* data) +{ + for (int i = 0; i < mac_timer_used; ++i) { + MacTimeout& t = mac_timers[i]; + if (t.timer == timer && t.data == data) { + t.pending = 0; + (*t.callback)(data); + if (t.pending==0) + delete_timer(t); + break; + } + } + breakMacEventLoop(); +} + + +@interface FLWindow : NSWindow { + Fl_Window *w; + BOOL containsGLsubwindow; +} +- (FLWindow*)initWithFl_W:(Fl_Window *)flw; +- (Fl_Window *)getFl_Window; +- (BOOL)windowShouldClose:(FLWindow *)w; +- (BOOL)containsGLsubwindow; +- (void)setContainsGLsubwindow:(BOOL)contains; +@end + +@implementation FLWindow +- (FLWindow*)initWithFl_W:(Fl_Window *)flw; +{ + w = flw; + containsGLsubwindow = NO; + return self; +} +- (Fl_Window *)getFl_Window; +{ + return w; +} +- (BOOL)windowShouldClose:(FLWindow *)fl +{ + Fl::handle( FL_CLOSE, [fl getFl_Window] ); // this might or might not close the window + if (!Fl_X::first) return YES; + Fl_Window *l = Fl::first_window(); + while( l != NULL && l != [fl getFl_Window]) l = Fl::next_window(l); + return (l == NULL ? YES : NO); +} +- (BOOL)containsGLsubwindow +{ + return containsGLsubwindow; +} +- (void)setContainsGLsubwindow:(BOOL)contains +{ + containsGLsubwindow = contains; +} +@end + +/** + * This function is the central event handler. + * It reads events from the event queue using the given maximum time + * Funny enough, it returns the same time that it got as the argument. + */ +static double do_queued_events( double time = 0.0 ) +{ + got_events = 0; + + // Check for re-entrant condition + if ( dataready.IsThreadRunning() ) + { dataready.CancelThread(DEBUGTEXT("AVOID REENTRY\n")); } + + // Start thread to watch for data ready + if ( dataready.GetNfds() ) + { dataready.StartThread((void*)GetCurrentEventQueue()); } + + fl_unlock_function(); + + //necessary so that after closing a non-FLTK window (e.g., Fl_Native_File_Chooser) + //the front window turns main again + Fl_Window *w = Fl::first_window(); + if (w) { + NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid; + if([cw isVisible] && ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { + if(![cw isKeyWindow]) {//always make Fl::first_window() the key window + [cw makeKeyAndOrderFront:nil]; + } + if(![cw isMainWindow]) {//always make Fl::first_window() the main window + [cw makeMainWindow]; + } + } + } + NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate dateWithTimeIntervalSinceNow:time] + inMode:NSDefaultRunLoopMode dequeue:YES]; + BOOL needSendEvent = YES; + if([event type] == NSLeftMouseDown) { + Fl_Window *grab = Fl::grab(); + if(grab && grab != [(FLWindow *)[event window] getFl_Window]) { + //a click event out of a menu window, so we should close this menu + //done here to catch also clicks on window title bar/resize box + cocoaMouseHandler(event); + } + } + else if([event type] == NSApplicationDefined) { + if([event subtype] == FLTKDataReadyEvent) { + processFLTKEvent(); + } + needSendEvent = NO; + } + if(needSendEvent) [NSApp sendEvent:event]; + fl_lock_function(); + +#if CONSOLIDATE_MOTION + if (send_motion && send_motion == fl_xmousewin) { + send_motion = 0; + Fl::handle(FL_MOVE, fl_xmousewin); + } +#endif + + return time; +} + + +/** + * This public function handles all events. It wait a maximum of + * 'time' seconds for an event. This version returns 1 if events + * other than the timeout timer were processed. + * + * \todo there is no socket handling in this code whatsoever + */ +int fl_wait( double time ) +{ + do_queued_events( time ); + return (got_events); +} + + + + +/** + * Cocoa Mousewheel handler + */ +void cocoaMouseWheelHandler(NSEvent *theEvent) +{ + // Handle the new "MightyMouse" mouse wheel events. Please, someone explain + // to me why Apple changed the API on this even though the current API + // supports two wheels just fine. Matthias, + fl_lock_function(); + + Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; + if ( !window->shown() ) + { + fl_unlock_function(); + return; + } + Fl::first_window(window); + + if([theEvent deltaX] != 0) { + Fl::e_dx = -[theEvent deltaX]; + Fl::e_dy = 0; + if ( Fl::e_dx) Fl::handle( FL_MOUSEWHEEL, window ); + } else if([theEvent deltaY] != 0) { + Fl::e_dx = 0; + Fl::e_dy = -[theEvent deltaY]; + if ( Fl::e_dy) Fl::handle( FL_MOUSEWHEEL, window ); + } else { + fl_unlock_function(); + + return; + } + + fl_unlock_function(); + + // return noErr; +} + + +/** + * Cocoa Mouse Button Handler + */ +static void cocoaMouseHandler(NSEvent *theEvent) +{ + static int keysym[] = { 0, FL_Button+1, FL_Button+3, FL_Button+2 }; + static int px, py; + static char suppressed = 0; + + fl_lock_function(); + + Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; + if ( !window->shown() ) + { + fl_unlock_function(); + return; + } + Fl_Window *first = Fl::first_window(); + if(first != window && !(first->modal() || first->non_modal())) Fl::first_window(window); + NSPoint pos = [theEvent locationInWindow]; + pos.y = window->h() - pos.y; + NSInteger btn = [theEvent buttonNumber] + 1; + int clickCount = [theEvent clickCount]; + NSUInteger mods = [theEvent modifierFlags]; + int sendEvent = 0; + + switch ( [theEvent type] ) + { + case NSLeftMouseDown: + case NSRightMouseDown: + case NSOtherMouseDown: + suppressed = 0; + sendEvent = FL_PUSH; + Fl::e_is_click = 1; + px = pos.x; py = pos.y; + if(btn == 1) Fl::e_state |= FL_BUTTON1; + else if(btn == 3) Fl::e_state |= FL_BUTTON2; + else if(btn == 2) Fl::e_state |= FL_BUTTON3; + if (clickCount>1) + Fl::e_clicks++; + else + Fl::e_clicks = 0; + // fall through + case NSLeftMouseUp: + case NSRightMouseUp: + case NSOtherMouseUp: + Fl::e_state &= 0xff0000; + if (suppressed) { + suppressed = 0; + break; + } + if ( !window ) break; + if ( !sendEvent ) { + sendEvent = FL_RELEASE; + } + Fl::e_keysym = keysym[ btn ]; + // fall through + case NSMouseMoved: + suppressed = 0; + if ( !sendEvent ) { + sendEvent = FL_MOVE; + } + // fall through + case NSLeftMouseDragged: + case NSRightMouseDragged: + case NSOtherMouseDragged: + if (suppressed) break; + if ( !sendEvent ) { + sendEvent = FL_MOVE; // Fl::handle will convert into FL_DRAG + if (abs(pos.x-px)>5 || abs(pos.y-py)>5) + Fl::e_is_click = 0; + } + mods_to_e_state( mods ); + NSPoint screen = [[theEvent window] convertBaseToScreen:[theEvent locationInWindow]]; + Fl::e_x_root = screen.x; + Fl::e_y_root = [[[theEvent window] screen] frame].size.height - screen.y; + Fl::e_x = pos.x; + Fl::e_y = pos.y; + Fl::handle( sendEvent, window ); + break; + } + + fl_unlock_function(); + + return; +} + + +/* + * keycode_function for post-10.5 systems, allows more sophisticated decoding of keys + */ +/* + static int keycodeToUnicode( + char * uniChars, int maxChars, + EventKind eKind, + UInt32 keycode, UInt32 modifiers, + UInt32 * deadKeyStatePtr, + unsigned char, // not used in this function + unsigned short) // not used in this function + { + // first get the keyboard mapping in a post 10.2 way + + Ptr resource; + TextEncoding encoding; + static TextEncoding lastEncoding = kTextEncodingMacRoman; + int len = 0; + KeyboardLayoutRef currentLayout = NULL; + static KeyboardLayoutRef lastLayout = NULL; + SInt32 currentLayoutId = 0; + static SInt32 lastLayoutId; + int hasLayoutChanged = false; + static Ptr uchr = NULL; + static Ptr KCHR = NULL; + // ScriptCode currentKeyScript; + + KLGetCurrentKeyboardLayout(¤tLayout); + if (currentLayout) { + KLGetKeyboardLayoutProperty(currentLayout, kKLIdentifier, (const void**)¤tLayoutId); + if ( (lastLayout != currentLayout) || (lastLayoutId != currentLayoutId) ) { + lastLayout = currentLayout; + lastLayoutId = currentLayoutId; + uchr = NULL; + KCHR = NULL; + if ((KLGetKeyboardLayoutProperty(currentLayout, kKLuchrData, (const void**)&uchr) == noErr) && (uchr != NULL)) { + // done + } else if ((KLGetKeyboardLayoutProperty(currentLayout, kKLKCHRData, (const void**)&KCHR) == noErr) && (KCHR != NULL)) { + // done + } + // FIXME No Layout property found. Now we have a problem. + } + } + if (hasLayoutChanged) { + //deadKeyStateUp = deadKeyStateDown = 0; + if (KCHR != NULL) { + // FIXME this must not happen + } else if (uchr == NULL) { + KCHR = (Ptr) GetScriptManagerVariable(smKCHRCache); + } + } + if (uchr != NULL) { + // this is what I expect + resource = uchr; + } else { + resource = KCHR; + encoding = lastEncoding; + // this is actually not supported by the following code and will likely crash + } + + // now apply that keyboard mapping to our keycode + + int action; + //OptionBits options = 0; + // not used yet: OptionBits options = kUCKeyTranslateNoDeadKeysMask; + unsigned long keyboardType; + keycode &= 0xFF; + modifiers = (modifiers >> 8) & 0xFF; + keyboardType = LMGetKbdType(); + OSStatus status; + UniCharCount actuallength; + UniChar utext[10]; + + switch(eKind) { + case kEventRawKeyDown: action = kUCKeyActionDown; break; + case kEventRawKeyUp: action = kUCKeyActionUp; break; + case kEventRawKeyRepeat: action = kUCKeyActionAutoKey; break; + default: return 0; + } + + UInt32 deadKeyState = *deadKeyStatePtr; + if ((action==kUCKeyActionUp)&&(*deadKeyStatePtr)) + deadKeyStatePtr = &deadKeyState; + + status = UCKeyTranslate( + (const UCKeyboardLayout *) uchr, + keycode, action, modifiers, keyboardType, + 0, deadKeyStatePtr, + 10, &actuallength, utext); + + if (noErr != status) { + fprintf(stderr,"UCKeyTranslate failed: %d\n", (int) status); + actuallength = 0; + } + + // convert the list of unicode chars into utf8 + // FIXME no bounds check (see maxchars) + unsigned i; + for (i=0; i= FL_KP && sym <= FL_KP_Last) || !(sym & 0xff00) || + sym == FL_Tab || sym == FL_Enter) { + buffer[0] = key; + return 1; + } else { + buffer[0] = 0; + return 0; + } +} /* keycode_wrap_old */ +/* + * Stub pointer to select appropriate keycode_function per operating system version. This function pointer + * is initialised in fl_open_display, based on the runtime identification of the host OS version. This is + * intended to allow us to utilise 10.5 services dynamically to improve Unicode handling, whilst still + * allowing code to run satisfactorily on older systems. + */ +static int (*keycode_function)(char*, int, EventKind, UInt32, UInt32, UInt32*, unsigned char, unsigned short) = keycode_wrap_old; + + +// EXPERIMENTAL! +//this gets called by CJK character palette input +OSStatus carbonTextHandler( EventHandlerCallRef nextHandler, EventRef event, void *unused ) +{ + //under 10.5 this gets called only after character palette inputs + //but under 10.6 this gets also called by interpretKeyEvents + //during character composition when we don't want to run it + if([[NSApp currentEvent] type] == NSKeyDown) return noErr; + if( [NSApp keyWindow] == nil) return noErr; + Fl_Window *window = [(FLWindow*)[NSApp keyWindow] getFl_Window]; + fl_lock_function(); + //int kind = GetEventKind(event); + unsigned short buf[200]; + ByteCount size; + GetEventParameter( event, kEventParamTextInputSendText, typeUnicodeText, + NULL, 100, &size, &buf ); + // printf("TextEvent: %02x %02x %02x %02x\n", buf[0], buf[1], buf[2], buf[3]); + // FIXME: oversimplified! + unsigned ucs = buf[0]; + char utf8buf[20]; + int len = fl_utf8encode(ucs, utf8buf); + + Fl::e_length = len; + Fl::e_text = utf8buf; + while (window->parent()) window = window->window(); + Fl::handle(FL_KEYBOARD, window); + fl_unlock_function(); + fl_lock_function(); + Fl::handle(FL_KEYUP, window); + fl_unlock_function(); + // for some reason, the window does not redraw until the next mouse move or button push + // sending a 'redraw()' or 'awake()' does not solve the issue! + Fl::flush(); + return noErr; +} + +static void processCompositionSequence(CFStringRef s, Fl_Window *window) +//composed character sequences are sent here +//they contain 2 unichars, the first comes from the deadkey and can be non ascii (e.g., diaeresis), +//the second is the character to be modified (e.g., e to be accented) +{ + //unicodes: non-ascii unicode chars produced by deadkeys + //asciis: corresponding ascii chars expected by Fl::compose() + // diaeresis acute-accent circumflex tilde ring-above + static UniChar unicodes[] = {0xA8, 0xB4, 0x2C6, 0x2DC, 0x2DA }; + static char asciis[] = { ':', '\'', '^', '~', '*' }; + if(CFStringGetLength(s) == 0) return; + char buffer[10]; + char text[10]; + UniChar first = CFStringGetCharacterAtIndex(s, 0); + CFStringGetCString(s, buffer, sizeof(buffer), kCFStringEncodingUTF8); + for(unsigned int i = 0; i < sizeof(asciis)/sizeof(char); i++) { + if(first == unicodes[i]) { + //replace the 2-byte utf8 of a non-ascii unicode by the corresponding 1-byte ascii + memmove(buffer+1, buffer+2, 2); + *buffer = asciis[i]; + break; + } + } + Fl::e_keysym = 0; + Fl::e_state = 0; + Fl::e_length = 1; + Fl::e_text = text; + Fl::e_text[0] = buffer[0]; + Fl::handle(FL_KEYBOARD, window); + Fl::e_keysym = 0; + Fl::e_state = 0; + Fl::e_length = 1; + Fl::e_text[0] = buffer[1]; + Fl::handle(FL_KEYBOARD, window); +} + + +/** + * handle cocoa keyboard events + */ +OSStatus cocoaKeyboardHandler(NSEvent *theEvent) +{ + static char buffer[32]; + int sendEvent = 0; + Fl_Window *window = (Fl_Window*)[(FLWindow*)[theEvent window] getFl_Window]; + Fl::first_window(window); + NSUInteger mods; + + fl_lock_function(); + + int kind = 0; + + // get the modifiers for any of the events + mods = [theEvent modifierFlags]; + + // get the key code only for key events + UInt32 keyCode = 0, maskedKeyCode = 0; + unichar key = 0; + unsigned char keychar = 0; + unsigned short sym = 0; + keyCode = [theEvent keyCode]; + NSString *s = [theEvent characters]; + static BOOL compose = NO; + static NSText *edit; + static int countevents; + static CFMutableStringRef sequence;//will contain the two characters of the composition sequence + if(compose) {//we are in a composition sequence + //the only benefit of sending events to the NSText object edit is that the deadkey becomes visible + //at its keyUp event; without this, the deadkey remains invisible + if([s length] == 0) {//occurs if 2 deadkeys are typed successively by error + compose = NO; + [edit setString:@""]; + CFRelease(sequence); + fl_unlock_function(); + return noErr; + } + [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; + countevents++; + UniChar deadkey; + CFRange range = {0, 1}; + CFStringGetCharacters((CFStringRef)s, range, &deadkey); + CFStringAppendCharacters(sequence, &deadkey, 1);//done for keyUp of deadkey and keyDown of next key + if(countevents >= 3) {//end of composition sequence + processCompositionSequence( sequence, window ); + CFRelease(sequence); + [edit setString:@""];//clear the content of the edit object + compose=NO;//character composition is now complete + } + fl_unlock_function(); + return noErr; + } + if([s length] == 0) {//this is a dead key that must be combined with the next key to be pressed + while (window->parent()) window = window->window(); + Fl::e_keysym = FL_Control_R;//first simulate pressing of the compose key (FL_Control_R) + Fl::e_text = ""; + Fl::e_length = 0; + Fl::handle(FL_KEYBOARD, window); + compose=YES; + //then send remaining events to an object of type NSText that helps handle character composition sequences + edit = [[theEvent window] fieldEditor:YES forObject:nil]; + [edit interpretKeyEvents:[NSArray arrayWithObject:theEvent]]; + countevents = 1; + sequence = CFStringCreateMutable(NULL, 2); + fl_unlock_function(); + return noErr; + } + else { + char buff[10]; + CFStringGetCString((CFStringRef)s, buff, sizeof(buff), kCFStringEncodingUnicode); + key = *(unichar*)buff; + keychar = buff[0]; + } + // extended keyboards can also send sequences on key-up to generate Kanji etc. codes. + // Some observed prefixes are 0x81 to 0x83, followed by an 8 bit keycode. + // In this mode, there seem to be no key-down codes + // printf("%08x %08x %08x\n", keyCode, mods, key); + maskedKeyCode = keyCode & 0x7f; + /* output a human readable event identifier for debugging + const char *ev = ""; + switch (kind) { + case kEventRawKeyDown: ev = "kEventRawKeyDown"; break; + case kEventRawKeyRepeat: ev = "kEventRawKeyRepeat"; break; + case kEventRawKeyUp: ev = "kEventRawKeyUp"; break; + case kEventRawKeyModifiersChanged: ev = "kEventRawKeyModifiersChanged"; break; + default: ev = "unknown"; + } + printf("%08x %08x %08x '%c' %s \n", mods, keyCode, key, key, ev); + */ + switch([theEvent type]) + { + case NSKeyDown: + sendEvent = FL_KEYBOARD; + // fall through + case NSKeyUp: + if ( !sendEvent ) { + sendEvent = FL_KEYUP; + Fl::e_state &= 0xbfffffff; // clear the deadkey flag + } + mods_to_e_state( mods ); //we process modifier keys at the same time + // if the user pressed alt/option, event_key should have the keycap, + // but event_text should generate the international symbol + sym = macKeyLookUp[maskedKeyCode]; + if ( isalpha(key) ) + sym = tolower(key); + else if ( Fl::e_state&FL_CTRL && key<32 && sym<0xff00) + sym = key+96; + /* else if ( Fl::e_state&FL_ALT && sym<0xff00) // find the keycap of this key + sym = maskedKeyCode;*/ + Fl::e_keysym = Fl::e_original_keysym = sym; + // Handle FL_KP_Enter on regular keyboards and on Powerbooks + if ( maskedKeyCode==0x4c || maskedKeyCode==0x34) key=0x0d; + static UInt32 deadKeyState = 0; // must be cleared when losing focus + int l = (*keycode_function)(buffer, 31, kind, keyCode, mods, &deadKeyState, keychar, sym); + if(l > 0) { + CFStringGetCString((CFStringRef)s, buffer, sizeof(buffer), kCFStringEncodingUTF8); + } + Fl::e_length = strlen(buffer); + Fl::e_text = buffer; + buffer[Fl::e_length] = 0; // just in case... + break; + } + while (window->parent()) window = window->window(); + if (sendEvent && Fl::handle(sendEvent,window)) { + fl_unlock_function(); + return noErr; // return noErr if FLTK handled the event + } else { + fl_unlock_function(); + return eventNotHandledErr; + } +} + + + +/** + * Open callback function to call... + */ + +static void (*open_cb)(const char *) = 0; + + +/** + * Install an open documents event handler... + */ +@interface FLAppleEventHandler : NSObject +{ +} +- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent; +@end +@implementation FLAppleEventHandler +- (void)handleAppleEvent:(NSAppleEventDescriptor *)event withReplyEvent: (NSAppleEventDescriptor *)replyEvent +{ + NSAppleEventDescriptor *single = [event descriptorAtIndex:1]; + const AEDesc *document = [single aeDesc]; + long i, n; + FSRef fileRef; + AEKeyword keyWd; + DescType typeCd; + Size actSz; + char filename[1024]; + // Lock access to FLTK in this thread... + fl_lock_function(); + + // Open the documents via the callback... + if (AECountItems(document, &n) == noErr) { + for (i = 1; i <= n; i ++) { + AEGetNthPtr(document, i, typeFSRef, &keyWd, &typeCd, + (Ptr)&fileRef, sizeof(fileRef), + (actSz = sizeof(fileRef), &actSz)); + FSRefMakePath( &fileRef, (UInt8*)filename, sizeof(filename) ); + + (*open_cb)(filename); + } + } + + // Unlock access to FLTK for all threads... + fl_unlock_function(); +} +@end + +void fl_open_callback(void (*cb)(const char *)) { + static NSAppleEventManager *aeventmgr = nil; + static FLAppleEventHandler *handler; + fl_open_display(); + if(!aeventmgr) { + aeventmgr = [NSAppleEventManager sharedAppleEventManager]; + handler = [[FLAppleEventHandler alloc] init]; + } + + open_cb = cb; + if (cb) { + [aeventmgr setEventHandler:handler andSelector: @selector(handleAppleEvent:withReplyEvent:) + forEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; + } else { + [aeventmgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments]; + } +} + + +/** + * initialize the Mac toolboxes, dock status, and set the default menubar + */ + +extern "C" { + extern OSErr CPSEnableForegroundOperation(ProcessSerialNumber *psn, UInt32 _arg2, + UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); +} + +@interface FLDelegate : NSObject { +} +- (void)windowDidMove:(NSNotification *)notif; +- (void)windowDidResize:(NSNotification *)notif; +- (void)windowDidBecomeKey:(NSNotification *)notif; +- (void)windowDidBecomeMain:(NSNotification *)notif; +- (void)windowDidDeminiaturize:(NSNotification *)notif; +- (void)windowDidMiniaturize:(NSNotification *)notif; +- (void)windowWillClose:(NSNotification *)notif; +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender; +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification; + +@end +@implementation FLDelegate +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification +{ + fl_system_menu = [NSApp mainMenu]; +} +- (void)windowDidMove:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + NSPoint pt, pt2; + pt.x = 0; + pt.y = [[nsw contentView] frame].size.height; + pt2 = [nsw convertBaseToScreen:pt]; + window->position(pt2.x, [[nsw screen] frame].size.height - pt2.y); + if([nsw containsGLsubwindow] ) { + [nsw display];//redraw window after moving if it contains OpenGL subwindows + } +} +- (void)windowDidResize:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + NSRect r = [[nsw contentView] frame]; + NSPoint pt, pt2; + pt.x = 0; + pt.y = [[nsw contentView] frame].size.height; + pt2 = [nsw convertBaseToScreen:pt]; + resize_from_system = window; + window->resize(pt2.x, [[nsw screen] frame].size.height - pt2.y, r.size.width, r.size.height); +} +- (void)windowDidBecomeKey:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + Fl::handle( FL_FOCUS, window); +} +- (void)windowDidBecomeMain:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + Fl::first_window(window); +} +- (void)windowDidDeminiaturize:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + window->set_visible(); +} +- (void)windowDidMiniaturize:(NSNotification *)notif +{ + FLWindow *nsw = (FLWindow*)[notif object]; + Fl_Window *window = [nsw getFl_Window]; + window->clear_visible(); +} +- (void)windowWillClose:(NSNotification *)notif +{ + Fl_Window *w = Fl::first_window(); + if(!w) return; + NSWindow *cw = (NSWindow*)Fl_X::i(w)->xid; + if( ![cw isMiniaturized] && ([cw styleMask] & NSTitledWindowMask) ) { + if(![cw isKeyWindow]) {//always make Fl::first_window() the key widow + [cw makeKeyAndOrderFront:nil]; + } + if(![cw isMainWindow]) {//always make Fl::first_window() the main widow + [cw makeMainWindow]; + } + } +} +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender +{ + fl_lock_function(); + NSApplicationTerminateReply reply = NSTerminateNow; + while ( Fl_X::first ) { + Fl_X *x = Fl_X::first; + Fl::handle( FL_CLOSE, x->w ); + if ( Fl_X::first == x ) { + reply = NSTerminateCancel; // FLTK has not closed all windows, so we return to the main program now + break; + } + } + fl_unlock_function(); + return reply; +} +@end + +static FLDelegate *mydelegate; + +void fl_open_display() { + static char beenHereDoneThat = 0; + if ( !beenHereDoneThat ) { + beenHereDoneThat = 1; + + [NSApplication sharedApplication]; + NSAutoreleasePool *localPool; + localPool = [[NSAutoreleasePool alloc] init]; + mydelegate = [[FLDelegate alloc] init]; + [NSApp setDelegate:mydelegate]; + [NSApp finishLaunching]; + + FlushEvents(everyEvent,0); + + fl_default_cursor = [NSCursor arrowCursor]; + Gestalt(gestaltSystemVersion, &MACsystemVersion); + + // bring the application into foreground without a 'CARB' resource + Boolean same_psn; + ProcessSerialNumber cur_psn, front_psn; + if( !GetCurrentProcess( &cur_psn ) && !GetFrontProcess( &front_psn ) && + !SameProcess( &front_psn, &cur_psn, &same_psn ) && !same_psn ) + { + // only transform the application type for unbundled apps + CFBundleRef bundle = CFBundleGetMainBundle(); + if( bundle ) + { + FSRef execFs; + CFURLRef execUrl = CFBundleCopyExecutableURL( bundle ); + CFURLGetFSRef( execUrl, &execFs ); + + FSRef bundleFs; + GetProcessBundleLocation( &cur_psn, &bundleFs ); + + if( !FSCompareFSRefs( &execFs, &bundleFs ) ) + bundle = NULL; + + CFRelease(execUrl); + } + + keycode_function = keycode_wrap_old; //under Cocoa we always use this one + /* // imm: keycode handler stub setting - use Gestalt to determine the running system version, + // then set the keycode_function pointer accordingly + if(MACsystemVersion >= 0x1050) { // 10.5.0 or later + keycode_function = keycodeToUnicode; + } + else { + keycode_function = keycode_wrap_old; // pre-10.5 mechanism + }*/ + + if( !bundle ) + { + // Earlier versions of this code tried to use weak linking, however it + // appears that this does not work on 10.2. Since 10.3 and higher provide + // both TransformProcessType and CPSEnableForegroundOperation, the following + // conditional code compiled on 10.2 will still work on newer releases... + OSErr err; +#if __LP64__ + err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); +#else + +#if MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 + if (TransformProcessType != NULL) { + err = TransformProcessType(&cur_psn, kProcessTransformToForegroundApplication); + } else +#endif // MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_2 + err = CPSEnableForegroundOperation(&cur_psn, 0x03, 0x3C, 0x2C, 0x1103); +#endif // __LP64__ + if (err == noErr) { + SetFrontProcess( &cur_psn ); + } + } + } + createAppleMenu(); + // Install Carbon Event handler for character palette input + static EventTypeSpec textEvents[] = { + { kEventClassTextInput, kEventTextInputUnicodeForKeyEvent } }; + EventHandlerUPP textHandler = NewEventHandlerUPP( carbonTextHandler ); + InstallEventHandler(GetEventDispatcherTarget(), textHandler, 1, textEvents, NULL, 0L); + } +} + + +/** + * get rid of allocated resources + */ +void fl_close_display() { +} + + +// Gets the border sizes and the titlebar size +static void get_window_frame_sizes(int &bx, int &by, int &bt) { + fl_open_display(); + NSRect inside = { {20,20}, {100,100} }; + NSRect outside = [NSWindow frameRectForContentRect:inside styleMask:NSTitledWindowMask]; + bx = outside.origin.x - inside.origin.x; + by = outside.origin.y - inside.origin.y; + bt = outside.size.height - inside.size.height - by; +} + +/** + * smallest x ccordinate in screen space + */ +int Fl::x() { + return [[NSScreen mainScreen] frame].origin.x; +} + + +/** + * smallest y coordinate in screen space + */ +int Fl::y() { + NSRect all = [[NSScreen mainScreen] frame]; + NSRect visible = [[NSScreen mainScreen] visibleFrame]; + return all.size.height - (visible.origin.y + visible.size.height); +} + + +/** + * screen width (single monitor!?) + */ +int Fl::w() { + return [[NSScreen mainScreen] visibleFrame].size.width; +} + + +/** + * screen height (single monitor!?) + */ +int Fl::h() { + int bx, by, bt; + get_window_frame_sizes(bx, by, bt); + return [[NSScreen mainScreen] frame].size.height - bt; +} + + +/** + * get the current mouse pointer world coordinates + */ +void Fl::get_mouse(int &x, int &y) +{ + fl_open_display(); + NSPoint pt = [NSEvent mouseLocation]; + x = pt.x; + y = [[NSScreen mainScreen] frame].size.height - pt.y; +} + + +/** + * convert Mac keystrokes to FLTK + */ +/* + unsigned short mac2fltk(ulong macKey) + { + unsigned short cc = macKeyLookUp[(macKey>>8)&0x7f]; + if (cc) return cc; + return macKey&0xff; + } + */ + +/** + * Initialize the given port for redraw and call the window's flush() to actually draw the content + */ +void Fl_X::flush() +{ + w->flush(); + if (fl_gc) { + CGContextFlush(fl_gc); + if(viewWithLockedFocus) { + [viewWithLockedFocus unlockFocus]; + viewWithLockedFocus = nil; + } + } +} + + +/** + * Gets called when a window is created, resized, or deminiaturized + */ +static void handleUpdateEvent( Fl_Window *window ) +{ + if ( !window ) return; + Fl_X *i = Fl_X::i( window ); + i->wait_for_expose = 0; + if ( i->region ) { + XDestroyRegion(i->region); + i->region = 0; + } + + for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) + { + if ( cx->region ) { + XDestroyRegion(cx->region); + cx->region = 0; + } + cx->w->clear_damage(FL_DAMAGE_ALL); + cx->flush(); + cx->w->clear_damage(); + } + window->clear_damage(FL_DAMAGE_ALL); + i->flush(); + window->clear_damage(); +} + +int Fl_X::fake_X_wm(const Fl_Window* w,int &X,int &Y, int &bt,int &bx, int &by) { + int W, H, xoff, yoff, dx, dy; + int ret = bx = by = bt = 0; + if (w->border() && !w->parent()) { + if (w->maxw != w->minw || w->maxh != w->minh) { + ret = 2; + get_window_frame_sizes(bx, by, bt); + } else { + ret = 1; + get_window_frame_sizes(bx, by, bt); + } + } + //The coordinates of the whole window, including non-client area + xoff = bx; + yoff = by + bt; + dx = 2*bx; + dy = 2*by + bt; + X = w->x()-xoff; + Y = w->y()-yoff; + W = w->w()+dx; + H = w->h()+dy; + + //Proceed to positioning the window fully inside the screen, if possible + + // let's get a little elaborate here. Mac OS X puts a lot of stuff on the desk + // that we want to avoid when positioning our window, namely the Dock and the + // top menu bar (and even more stuff in 10.4 Tiger). So we will go through the + // list of all available screens and find the one that this window is most + // likely to go to, and then reposition it to fit withing the 'good' area. + // Rect r; + // find the screen, that the center of this window will fall into + int R = X+W, B = Y+H; // right and bottom + int cx = (X+R)/2, cy = (Y+B)/2; // center of window; + NSScreen *gd = NULL; + NSArray *a = [NSScreen screens]; int count = (int)[a count]; NSRect r; int i; + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + cy = r.size.height - cy; + if ( cx >= r.origin.x && cx <= r.origin.x + r.size.width + && cy >= r.origin.y && cy <= r.origin.y + r.size.height) + break; + } + if(i < count) gd = [a objectAtIndex:i]; + + // if the center doesn't fall on a screen, try the top left + if (!gd) { + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + if ( X >= r.origin.x && X <= r.origin.x + r.size.width + && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) + break; + } + if(i < count) gd = [a objectAtIndex:i]; + } + // if that doesn't fall on a screen, try the top right + if (!gd) { + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + if ( R >= r.origin.x && R <= r.origin.x + r.size.width + && r.size.height - Y >= r.origin.y && r.size.height - Y <= r.origin.y + r.size.height) + break; + } + if(i < count) gd = [a objectAtIndex:i]; + } + // if that doesn't fall on a screen, try the bottom left + if (!gd) { + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + if ( X >= r.origin.x && X <= r.origin.x + r.size.width + && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) + break; + } + if(i < count) gd = [a objectAtIndex:i]; + } + // last resort, try the bottom right + if (!gd) { + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + if ( R >= r.origin.x && R <= r.origin.x + r.size.width + && Y-H >= r.origin.y && Y-H <= r.origin.y + r.size.height) + break; + } + if(i < count) gd = [a objectAtIndex:i]; + } + // if we still have not found a screen, we will use the main + // screen, the one that has the application menu bar. + if (!gd) gd = [a objectAtIndex:0]; + if (gd) { + r = [gd visibleFrame]; + int sh = [gd frame].size.height; + if ( R > r.origin.x + r.size.width ) X -= R - (r.origin.x + r.size.width); + if ( B > sh - r.origin.y ) Y -= B - (sh - r.origin.y); + if ( X < r.origin.x ) X = r.origin.x; + if ( Y < sh - (r.origin.y + r.size.height) ) Y = sh - (r.origin.y + r.size.height); + } + + //Return the client area's top left corner in (X,Y) + X+=xoff; + Y+=yoff; + + return ret; +} + + +Fl_Window *fl_dnd_target_window = 0; + +static void q_set_window_title(NSWindow *nsw, const char * name ) { + CFStringRef utf8_title = CFStringCreateWithCString(NULL, (name ? name : ""), kCFStringEncodingUTF8); + [nsw setTitle:(NSString*)utf8_title ]; + CFRelease(utf8_title); +} + + +@interface FLView : NSView { +} +- (void)drawRect:(NSRect)rect; +- (BOOL)acceptsFirstResponder; +- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent; +- (BOOL)performKeyEquivalent:(NSEvent*)theEvent; +- (void)mouseUp:(NSEvent *)theEvent; +- (void)rightMouseUp:(NSEvent *)theEvent; +- (void)otherMouseUp:(NSEvent *)theEvent; +- (void)mouseDown:(NSEvent *)theEvent; +- (void)rightMouseDown:(NSEvent *)theEvent; +- (void)otherMouseDown:(NSEvent *)theEvent; +- (void)mouseDragged:(NSEvent *)theEvent; +- (void)scrollWheel:(NSEvent *)theEvent; +- (void)keyDown:(NSEvent *)theEvent; +- (void)keyUp:(NSEvent *)theEvent; +- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender; +- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender; +- (BOOL)performDragOperation:(id )sender; +- (void)draggingExited:(id < NSDraggingInfo >)sender; +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal; +@end + +@implementation FLView +- (void)drawRect:(NSRect)rect +{ + FLWindow *cw = (FLWindow*)[self window]; + Fl_Window *w = [cw getFl_Window]; + handleUpdateEvent(w); +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (BOOL)performKeyEquivalent:(NSEvent*)theEvent +{ + OSStatus err = cocoaKeyboardHandler(theEvent); + return (err ? NO : YES); +} +- (BOOL)acceptsFirstMouse:(NSEvent*)theEvent +{ + Fl_Window *w = [(FLWindow*)[theEvent window] getFl_Window]; + Fl_Window *first = Fl::first_window(); + return (first == w || !first->modal()); +} +- (void)mouseUp:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)rightMouseUp:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)otherMouseUp:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)mouseDown:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)rightMouseDown:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)otherMouseDown:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)mouseMoved:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)mouseDragged:(NSEvent *)theEvent { + cocoaMouseHandler(theEvent); +} +- (void)scrollWheel:(NSEvent *)theEvent { + cocoaMouseWheelHandler(theEvent); +} +- (void)keyDown:(NSEvent *)theEvent { + cocoaKeyboardHandler(theEvent); +} +- (void)keyUp:(NSEvent *)theEvent { + cocoaKeyboardHandler(theEvent); +} +- (NSDragOperation)draggingEntered:(id < NSDraggingInfo >)sender +{ + Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; + NSPoint pt = [sender draggingLocation]; + Fl::e_x = pt.x; + Fl::e_y = [self frame].size.height - pt.y; + Fl::e_x_root = [[self window] frame].origin.x + Fl::e_x; + Fl::e_y_root = [[self window] frame].origin.y + pt.y; + Fl::e_y_root = [[[self window] screen] frame].size.height - Fl::e_y_root; + fl_dnd_target_window = target; + Fl::handle( FL_DND_ENTER, target ); + return NSDragOperationGeneric; +} +- (NSDragOperation)draggingUpdated:(id < NSDraggingInfo >)sender +{ + Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; + NSPoint pt = [sender draggingLocation]; + Fl::e_x = pt.x; + Fl::e_y = [self frame].size.height - pt.y; + Fl::e_x_root = [[self window] frame].origin.x + Fl::e_x; + Fl::e_y_root = [[self window] frame].origin.y + pt.y; + Fl::e_y_root = [[[self window] screen] frame].size.height - Fl::e_y_root; + fl_dnd_target_window = target; + Fl::handle( FL_DND_DRAG, target ); + return NSDragOperationGeneric; +} +- (BOOL)performDragOperation:(id )sender +{ + static char *DragData = NULL; + Fl_Window *target = [(FLWindow*)[self window] getFl_Window]; + if ( !Fl::handle( FL_DND_RELEASE, target ) ) { return NO; } + NSPasteboard *pboard; + //NSDragOperation sourceDragMask; + //sourceDragMask = [sender draggingSourceOperationMask]; + pboard = [sender draggingPasteboard]; + NSPoint pt = [sender draggingLocation]; + Fl::e_x = pt.x; + Fl::e_y = [self frame].size.height - pt.y; + Fl::e_x_root = [[self window] frame].origin.x + Fl::e_x; + Fl::e_y_root = [[self window] frame].origin.y + pt.y; + Fl::e_y_root = [[[self window] screen] frame].size.height - Fl::e_y_root; + if(DragData) { free(DragData); DragData = NULL; } + if ( [[pboard types] containsObject:NSFilenamesPboardType] ) { + CFArrayRef files = (CFArrayRef)[pboard propertyListForType:NSFilenamesPboardType]; + CFStringRef all = CFStringCreateByCombiningStrings(NULL, files, CFSTR("\n")); + int l = CFStringGetMaximumSizeForEncoding(CFStringGetLength(all), kCFStringEncodingUTF8); + DragData = (char *)malloc(l + 1); + CFStringGetCString(all, DragData, l + 1, kCFStringEncodingUTF8); + CFRelease(all); + } + else if ( [[pboard types] containsObject:NSStringPboardType] ) { + NSData *data = [pboard dataForType:NSStringPboardType]; + DragData = (char *)malloc([data length] + 1); + [data getBytes:DragData]; + DragData[[data length]] = 0; + convert_crlf(DragData, strlen(DragData)); + } + else return NO; + Fl::e_text = DragData; + Fl::e_length = strlen(DragData); + int old_event = Fl::e_number; + Fl::belowmouse()->handle(Fl::e_number = FL_PASTE); + Fl::e_number = old_event; + if(DragData) { free(DragData); DragData = NULL; } + Fl::e_text = NULL; + Fl::e_length = 0; + fl_dnd_target_window = NULL; + return YES; +} +- (void)draggingExited:(id < NSDraggingInfo >)sender +{ + if ( fl_dnd_target_window ) + { + Fl::handle( FL_DND_LEAVE, fl_dnd_target_window ); + fl_dnd_target_window = 0; + } +} +- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal +{ + return NSDragOperationGeneric; +} +@end + + +/** + * go ahead, create that (sub)window + * \todo we should make menu windows slightly transparent for the new Mac look + */ +void Fl_X::make(Fl_Window* w) +{ + static int xyPos = 100; + if ( w->parent() ) // create a subwindow + { + Fl_Group::current(0); + Rect wRect; + wRect.top = w->y(); + wRect.left = w->x(); + wRect.bottom = w->y() + w->h(); if (wRect.bottom<=wRect.top) wRect.bottom = wRect.top+1; + wRect.right = w->x() + w->w(); if (wRect.right<=wRect.left) wRect.right = wRect.left+1; + // our subwindow needs this structure to know about its clipping. + Fl_X* x = new Fl_X; + x->other_xid = 0; + x->region = 0; + x->subRegion = 0; + x->cursor = fl_default_cursor; + x->gc = 0; // stay 0 for Quickdraw; fill with CGContext for Quartz + Fl_Window *win = w->window(); + Fl_X *xo = Fl_X::i(win); + if (xo) { + x->xidNext = xo->xidChildren; + x->xidChildren = 0L; + xo->xidChildren = x; + x->xid = win->i->xid; + x->w = w; w->i = x; + x->wait_for_expose = 0; + Fl_X *z = xo->next; //we don't want a subwindow in Fl_X::first + xo->next = x; + x->next = z; + int old_event = Fl::e_number; + w->handle(Fl::e_number = FL_SHOW); + Fl::e_number = old_event; + w->redraw(); // force draw to happen + } + fl_show_iconic = 0; + } + else // create a desktop window + { + NSAutoreleasePool *localPool; + localPool = [[NSAutoreleasePool alloc] init]; + Fl_Group::current(0); + fl_open_display(); + NSInteger winlevel = NSNormalWindowLevel; + NSUInteger winstyle; + if(w->border()) winstyle = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask; + else winstyle = NSBorderlessWindowMask; + int xp = w->x(); + int yp = w->y(); + int wp = w->w(); + int hp = w->h(); + if (w->size_range_set) { + if ( w->minh != w->maxh || w->minw != w->maxw) + winstyle |= NSResizableWindowMask; + } else { + if (w->resizable()) { + Fl_Widget *o = w->resizable(); + int minw = o->w(); if (minw > 100) minw = 100; + int minh = o->h(); if (minh > 100) minh = 100; + w->size_range(w->w() - o->w() + minw, w->h() - o->h() + minh, 0, 0); + winstyle |= NSResizableWindowMask; + } else { + w->size_range(w->w(), w->h(), w->w(), w->h()); + } + } + int xwm = xp, ywm = yp, bt, bx, by; + + if (!fake_X_wm(w, xwm, ywm, bt, bx, by)) { + // menu windows and tooltips + if (w->modal()||w->override()) { + winstyle = NSBorderlessWindowMask; + winlevel = NSMainMenuWindowLevel; + } else { + winstyle = NSBorderlessWindowMask; + } + } else if (w->modal()) { + winstyle &= ~(NSResizableWindowMask | NSMiniaturizableWindowMask); + // winlevel = NSModalPanelWindowLevel; + } + else if (w->non_modal()) { + winlevel = NSFloatingWindowLevel; + } + + if (by+bt) { + wp += 2*bx; + hp += 2*by+bt; + } + if (!(w->flags() & Fl_Window::FORCE_POSITION)) { + // use the Carbon functions below for default window positioning + w->x(xyPos+Fl::x()); + w->y(xyPos+Fl::y()); + xyPos += 25; + if (xyPos>200) xyPos = 100; + } else { + if (!Fl::grab()) { + xp = xwm; yp = ywm; + w->x(xp);w->y(yp); + } + xp -= bx; + yp -= by+bt; + } + + if (w->non_modal() && Fl_X::first && !fl_disable_transient_for) { + // find some other window to be "transient for": + Fl_Window* w = Fl_X::first->w; + while (w->parent()) w = w->window(); // todo: this code does not make any sense! (w!=w??) + } + + Rect wRect; + wRect.top = w->y(); + wRect.left = w->x(); + wRect.bottom = w->y() + w->h(); if (wRect.bottom<=wRect.top) wRect.bottom = wRect.top+1; + wRect.right = w->x() + w->w(); if (wRect.right<=wRect.left) wRect.right = wRect.left+1; + + const char *name = w->label(); + + Fl_X* x = new Fl_X; + x->other_xid = 0; // room for doublebuffering image map. On OS X this is only used by overlay windows + x->region = 0; + x->subRegion = 0; + x->cursor = fl_default_cursor; + x->xidChildren = 0; + x->xidNext = 0; + x->gc = 0; + + FLWindow *cw = [[FLWindow alloc] initWithFl_W:w]; + NSRect srect = [[NSScreen mainScreen] frame]; + NSRect crect; + crect.origin.x = w->x(); + crect.origin.y = srect.size.height - (w->y() + w->h()); + crect.size.width=w->w(); + crect.size.height=w->h(); + [cw initWithContentRect:crect styleMask:winstyle backing:NSBackingStoreBuffered defer:NO]; + [cw setAcceptsMouseMovedEvents:YES]; + x->xid = cw; + FLView *myview = [[FLView alloc] init]; + [cw setContentView:myview]; + [cw setLevel:winlevel]; + + q_set_window_title(cw, name); + if (!(w->flags() & Fl_Window::FORCE_POSITION)) + { + if (w->modal()) { + [cw center]; + } else if (w->non_modal()) { + [cw center]; + } else { + static NSPoint delta = NSZeroPoint; + delta = [cw cascadeTopLeftFromPoint:delta]; + } + } + x->w = w; w->i = x; + x->wait_for_expose = 1; + x->next = Fl_X::first; + Fl_X::first = x; + // Install DnD handlers + [myview registerForDraggedTypes:[NSArray arrayWithObjects: + NSStringPboardType, NSFilenamesPboardType, nil]]; + if ( ! Fl_X::first->next ) // if this is the first window, we need to bring the application to the front + { + ProcessSerialNumber psn; + OSErr err = GetCurrentProcess( &psn ); + if ( err==noErr ) SetFrontProcess( &psn ); + } + + if (w->size_range_set) w->size_range_(); + + if(winlevel != NSMainMenuWindowLevel) { + Fl_Tooltip::enter(0); + } + [cw makeKeyAndOrderFront:nil]; + Fl::first_window(w); + [cw setDelegate:mydelegate]; + if (fl_show_iconic) { + fl_show_iconic = 0; + [cw miniaturize:nil]; + } else { + w->set_visible(); + } + + crect = [[cw contentView] frame]; + w->w(crect.size.width); + w->h(crect.size.height); + crect = [cw frame]; + w->x(crect.origin.x); + srect = [[cw screen] frame]; + w->y(srect.size.height - (crect.origin.y + w->h())); + + int old_event = Fl::e_number; + w->handle(Fl::e_number = FL_SHOW); + Fl::e_number = old_event; + + if (w->modal()) { Fl::modal_ = w; fl_fix_focus(); } + [localPool release]; + } +} + + +/** + * Tell the OS what window sizes we want to allow + */ +void Fl_Window::size_range_() { + int bx, by, bt; + get_window_frame_sizes(bx, by, bt); + size_range_set = 1; + NSSize minSize = { minw, minh + bt }; + NSSize maxSize = { maxw?maxw:32000, maxh?maxh + bt:32000 }; + if (i && i->xid) { + [(NSWindow*)i->xid setMinSize:minSize]; + [(NSWindow*)i->xid setMaxSize:maxSize]; + } +} + + +/** + * returns pointer to the filename, or null if name ends with ':' + */ +const char *fl_filename_name( const char *name ) +{ + const char *p, *q; + if (!name) return (0); + for ( p = q = name ; *p ; ) + { + if ( ( p[0] == ':' ) && ( p[1] == ':' ) ) + { + q = p+2; + p++; + } + else if (p[0] == '/') + q = p + 1; + p++; + } + return q; +} + + +/** + * set the window title bar + * \todo make the titlebar icon work! + */ +void Fl_Window::label(const char *name,const char */*iname*/) { + Fl_Widget::label(name); + if (shown() || i) { + q_set_window_title((NSWindow*)i->xid, name); + } +} + + +/** + * make a window visible + */ +void Fl_Window::show() { + image(Fl::scheme_bg_); + if (Fl::scheme_bg_) { + labeltype(FL_NORMAL_LABEL); + align(FL_ALIGN_CENTER | FL_ALIGN_INSIDE | FL_ALIGN_CLIP); + } else { + labeltype(FL_NO_LABEL); + } + Fl_Tooltip::exit(this); + if (!shown() || !i) { + Fl_X::make(this); + } else { + if ( !parent() ) + { + if([(NSWindow*)i->xid isMiniaturized]) { + i->w->redraw(); + [(NSWindow*)i->xid deminiaturize:nil]; + } + + if (!fl_capture) { + [(NSWindow*)i->xid makeKeyAndOrderFront:nil]; + } + } + } +} + + +/** + * resize a window + */ +void Fl_Window::resize(int X,int Y,int W,int H) { + int bx, by, bt; + if( ! this->border() ) bt = 0; + else get_window_frame_sizes(bx, by, bt); + if (W<=0) W = 1; // OS X does not like zero width windows + if (H<=0) H = 1; + int is_a_resize = (W != w() || H != h()); + // printf("Fl_Window::resize(X=%d, Y=%d, W=%d, H=%d), is_a_resize=%d, resize_from_system=%p, this=%p\n", + // X, Y, W, H, is_a_resize, resize_from_system, this); + if (X != x() || Y != y()) set_flag(FORCE_POSITION); + else if (!is_a_resize) return; + if ( (resize_from_system!=this) && (!parent()) && shown()) { + if (is_a_resize) { + if (resizable()) { + if (Wmaxw) maxw = W; // over a previously set size_range + if (Hmaxh) maxh = H; + size_range(minw, minh, maxw, maxh); + } else { + size_range(W, H, W, H); + } + NSRect dim; + dim.origin.x = X; + dim.origin.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + H); + dim.size.width = W; + dim.size.height = H + bt; + [(NSWindow*)i->xid setFrame:dim display:YES]; + } else { + NSPoint pt; + pt.x = X; + pt.y = [[(NSWindow*)i->xid screen] frame].size.height - (Y + h()); + [(NSWindow*)i->xid setFrameOrigin:pt]; + } + } + resize_from_system = 0; + if (is_a_resize) { + Fl_Group::resize(X,Y,W,H); + if (shown()) { + redraw(); + } + } else { + x(X); y(Y); + } +} + + +/** + * make all drawing go into this window (called by subclass flush() impl.) + */ +void Fl_Window::make_current() +{ + Fl_X::q_release_context(); + fl_window = i->xid; + current_ = this; + + int xp = 0, yp = 0; + Fl_Window *win = this; + while ( win ) + { + if ( !win->window() ) + break; + xp += win->x(); + yp += win->y(); + win = (Fl_Window*)win->window(); + } + + viewWithLockedFocus = [(NSWindow*)i->xid contentView]; + [viewWithLockedFocus lockFocusIfCanDraw];//important + i->gc = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; + fl_gc = i->gc; + if ( fl_window_region ) XDestroyRegion(fl_window_region); + if(this->window()) { + fl_window_region = XRectangleRegion(0,0,w(),h()); + } + else { + fl_window_region = XRectangleRegion(0, 0, w(), h()); + for ( Fl_X *cx = i->xidChildren; cx; cx = cx->xidNext ) + {//clip-out all sub-windows + Fl_Window *cw = cx->w; + Fl_Region from = fl_window_region; + fl_window_region = MacRegionMinusRect(from, cw->x(), cw->y(), cw->w(), cw->h() ); + XDestroyRegion(from); + } + } + + //antialiasing must be deactivated because it applies to rectangles too + //and escapes even clipping!!! + //it gets activated when needed (e.g., draw text) + CGContextSetShouldAntialias(fl_gc, false); + //this is the native view context with origin at bottom left + CGContextSaveGState(fl_gc); +#if defined(USE_CAIRO) + if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); // capture gc changes automatically to update the cairo context adequately +#endif + fl_clip_region( 0 ); + +#if defined(USE_CAIRO) + // update the cairo_t context + if (Fl::cairo_autolink_context()) Fl::cairo_make_current(this); +#endif +} + +// helper function to manage the current CGContext fl_gc +extern Fl_Color fl_color_; +extern class Fl_Font_Descriptor *fl_fontsize; +extern void fl_font(class Fl_Font_Descriptor*); +extern void fl_quartz_restore_line_style_(); + +// FLTK has only one global graphics state. This function copies the FLTK state into the +// current Quartz context +void Fl_X::q_fill_context() { + if (!fl_gc) return; + int hgt = 0; + if (fl_window) { + hgt = [[(NSWindow*)Fl_X::i(Fl_Window::current_)->xid contentView] frame].size.height; + } else { + hgt = CGBitmapContextGetHeight(fl_gc); + } + CGContextTranslateCTM(fl_gc, 0.5, hgt-0.5f); + CGContextScaleCTM(fl_gc, 1.0f, -1.0f); // now 0,0 is top-left point of the context + fl_font(fl_fontsize); + fl_color(fl_color_); + fl_quartz_restore_line_style_(); + if (fl_window) {//translate to subwindow origin if this is a subwindow context + Fl_Window *w = Fl_Window::current_; + while(w && w->window()) { + CGContextTranslateCTM(fl_gc, w->x(), w->y()); + w = w->window(); + } + } +} + +// The only way to reset clipping to its original state is to pop the current graphics +// state and restore the global state. +void Fl_X::q_clear_clipping() { + if (!fl_gc) return; + CGContextRestoreGState(fl_gc); + CGContextSaveGState(fl_gc); +} + +// Give the Quartz context back to the system +void Fl_X::q_release_context(Fl_X *x) { + if (x && x->gc!=fl_gc) return; + if (!fl_gc) return; + CGContextRestoreGState(fl_gc); //matches the CGContextSaveGState of make_current + fl_gc = 0; +#if defined(USE_CAIRO) + if (Fl::cairo_autolink_context()) Fl::cairo_make_current((Fl_Window*) 0); // capture gc changes automatically to update the cairo context adequately +#endif +} + +void Fl_X::q_begin_image(CGRect &rect, int cx, int cy, int w, int h) { + CGContextSaveGState(fl_gc); + CGAffineTransform mx = CGContextGetCTM(fl_gc); + CGRect r2 = rect; + r2.origin.x -= 0.5f; + r2.origin.y -= 0.5f; + CGContextClipToRect(fl_gc, r2); + mx.d = -1.0; mx.tx = -mx.tx; + CGContextConcatCTM(fl_gc, mx); + rect.origin.x = -(mx.tx+0.5f) + rect.origin.x - cx; + rect.origin.y = (mx.ty+0.5f) - rect.origin.y - h + cy; + rect.size.width = w; + rect.size.height = h; +} + +void Fl_X::q_end_image() { + CGContextRestoreGState(fl_gc); +} + + +//////////////////////////////////////////////////////////////// +// Copy & Paste fltk implementation. +//////////////////////////////////////////////////////////////// + +static void convert_crlf(char * s, size_t len) +{ + // turn all \r characters into \n: + for (size_t x = 0; x < len; x++) if (s[x] == '\r') s[x] = '\n'; +} + +// fltk 1.3 clipboard support constant definitions: +const CFStringRef flavorNames[] = { + CFSTR("public.utf16-plain-text"), + CFSTR("public.utf8-plain-text"), + CFSTR("com.apple.traditional-mac-plain-text") }; +const CFStringEncoding encodings[] = { + kCFStringEncodingUnicode, + kCFStringEncodingUTF8, + kCFStringEncodingMacRoman}; +const size_t handledFlavorsCount = sizeof(encodings)/sizeof(CFStringEncoding); + +// clipboard variables definitions : +Fl_Widget *fl_selection_requestor = 0; +char *fl_selection_buffer[2]; +int fl_selection_length[2]; +static int fl_selection_buffer_length[2]; + +static PasteboardRef myPasteboard = 0; +static void allocatePasteboard() { + if (!myPasteboard) + PasteboardCreate(kPasteboardClipboard, &myPasteboard); +} + + +/** + * create a selection + * owner: widget that created the selection + * stuff: pointer to selected data + * size of selected data + */ +void Fl::copy(const char *stuff, int len, int clipboard) { + if (!stuff || len<0) return; + if (len+1 > fl_selection_buffer_length[clipboard]) { + delete[] fl_selection_buffer[clipboard]; + fl_selection_buffer[clipboard] = new char[len+100]; + fl_selection_buffer_length[clipboard] = len+100; + } + memcpy(fl_selection_buffer[clipboard], stuff, len); + fl_selection_buffer[clipboard][len] = 0; // needed for direct paste + fl_selection_length[clipboard] = len; + if (clipboard) { + allocatePasteboard(); + OSStatus err = PasteboardClear(myPasteboard); + if (err!=noErr) return; // clear did not work, maybe not owner of clipboard. + PasteboardSynchronize(myPasteboard); + CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[1], len); + if (text==NULL) return; // there was a pb creating the object, abort. + err=PasteboardPutItemFlavor(myPasteboard, (PasteboardItemID)1, CFSTR("public.utf8-plain-text"), text, 0); + CFRelease(text); + } +} + +// Call this when a "paste" operation happens: +void Fl::paste(Fl_Widget &receiver, int clipboard) { + if (clipboard) { + // see if we own the selection, if not go get it: + fl_selection_length[1] = 0; + OSStatus err = noErr; + Boolean found = false; + CFDataRef flavorData = NULL; + CFStringEncoding encoding = 0; + + allocatePasteboard(); + PasteboardSynchronize(myPasteboard); + ItemCount nFlavor = 0, i, j; + err = PasteboardGetItemCount(myPasteboard, &nFlavor); + if (err==noErr) { + for (i=1; i<=nFlavor; i++) { + PasteboardItemID itemID = 0; + CFArrayRef flavorTypeArray = NULL; + found = false; + err = PasteboardGetItemIdentifier(myPasteboard, i, &itemID); + if (err!=noErr) continue; + err = PasteboardCopyItemFlavors(myPasteboard, itemID, &flavorTypeArray); + if (err!=noErr) { + if (flavorTypeArray) {CFRelease(flavorTypeArray); flavorTypeArray = NULL;} + continue; + } + CFIndex flavorCount = CFArrayGetCount(flavorTypeArray); + for (j = 0; j < handledFlavorsCount; j++) { + for (CFIndex flavorIndex=0; flavorIndex= fl_selection_buffer_length[1] ) { + fl_selection_buffer_length[1] = len; + delete[] fl_selection_buffer[1]; + fl_selection_buffer[1] = new char[len]; + } + CFStringGetCString(mycfs, fl_selection_buffer[1], len, kCFStringEncodingUTF8); + CFRelease(mycfs); + len = strlen(fl_selection_buffer[1]); + fl_selection_length[1] = len; + convert_crlf(fl_selection_buffer[1],len); // turn all \r characters into \n: + } + } + } + Fl::e_text = fl_selection_buffer[clipboard]; + Fl::e_length = fl_selection_length[clipboard]; + if (!Fl::e_text) Fl::e_text = (char *)""; + receiver.handle(FL_PASTE); +} + +void Fl::add_timeout(double time, Fl_Timeout_Handler cb, void* data) +{ + // check, if this timer slot exists already + for (int i = 0; i < mac_timer_used; ++i) { + MacTimeout& t = mac_timers[i]; + // if so, simply change the fire interval + if (t.callback == cb && t.data == data) { + SetEventLoopTimerNextFireTime(t.timer, (EventTimerInterval)time); + t.pending = 1; + return; + } + } + // no existing timer to use. Create a new one: + int timer_id = -1; + // find an empty slot in the timer array + for (int i = 0; i < mac_timer_used; ++i) { + if ( !mac_timers[i].timer ) { + timer_id = i; + break; + } + } + // if there was no empty slot, append a new timer + if (timer_id == -1) { + // make space if needed + if (mac_timer_used == mac_timer_alloc) { + realloc_timers(); + } + timer_id = mac_timer_used++; + } + // now install a brand new timer + MacTimeout& t = mac_timers[timer_id]; + EventTimerInterval fireDelay = (EventTimerInterval)time; + EventLoopTimerUPP timerUPP = NewEventLoopTimerUPP(do_timer); + EventLoopTimerRef timerRef = 0; + OSStatus err = InstallEventLoopTimer(GetMainEventLoop(), fireDelay, 0, timerUPP, data, &timerRef); + if (err == noErr) { + t.callback = cb; + t.data = data; + t.timer = timerRef; + t.upp = timerUPP; + t.pending = 1; + } else { + if (timerRef) + RemoveEventLoopTimer(timerRef); + if (timerUPP) + DisposeEventLoopTimerUPP(timerUPP); + } +} + +void Fl::repeat_timeout(double time, Fl_Timeout_Handler cb, void* data) +{ + // currently, repeat_timeout does not subtract the trigger time of the previous timer event as it should. + add_timeout(time, cb, data); +} + +int Fl::has_timeout(Fl_Timeout_Handler cb, void* data) +{ + for (int i = 0; i < mac_timer_used; ++i) { + MacTimeout& t = mac_timers[i]; + if (t.callback == cb && t.data == data && t.pending) { + return 1; + } + } + return 0; +} + +void Fl::remove_timeout(Fl_Timeout_Handler cb, void* data) +{ + for (int i = 0; i < mac_timer_used; ++i) { + MacTimeout& t = mac_timers[i]; + if (t.callback == cb && ( t.data == data || data == NULL)) { + delete_timer(t); + } + } + breakMacEventLoop(); +} + +int MacUnlinkWindow(Fl_X *ip, Fl_X *start) { + if (!ip) return 0; + if (start) { + Fl_X *pc = start; + while (pc) { + if (pc->xidNext == ip) { + pc->xidNext = ip->xidNext; + return 1; + } + if (pc->xidChildren) { + if (pc->xidChildren == ip) { + pc->xidChildren = ip->xidNext; + return 1; + } + if (MacUnlinkWindow(ip, pc->xidChildren)) + return 1; + } + pc = pc->xidNext; + } + } else { + for ( Fl_X *pc = Fl_X::first; pc; pc = pc->next ) { + if (MacUnlinkWindow(ip, pc)) + return 1; + } + } + return 0; +} + +static void MacRelinkWindow(Fl_X *x, Fl_X *p) { + if (!x || !p) return; + // first, check if 'x' is already registered as a child of 'p' + for (Fl_X *i = p->xidChildren; i; i=i->xidNext) { + if (i == x) return; + } + // now add 'x' as the first child of 'p' + x->xidNext = p->xidChildren; + p->xidChildren = x; +} + +void MacDestroyWindow(Fl_Window *w, void *p) { + MacUnmapWindow(w, p); + if (w && !w->parent() && p) { + [[(NSWindow *)p contentView] release]; + [(NSWindow *)p close]; + } +} + +void MacMapWindow(Fl_Window *w, void *p) { + if (w && p) { + [(NSWindow *)p orderFront:nil]; + } + //+ link to window list + if (w && w->parent()) { + MacRelinkWindow(Fl_X::i(w), Fl_X::i(w->window())); + w->redraw(); + } +} + +void MacUnmapWindow(Fl_Window *w, void *p) { + if (w && !w->parent() && p) { + [(NSWindow *)p orderOut:nil]; + } + if (w && Fl_X::i(w)) + MacUnlinkWindow(Fl_X::i(w)); +} + +Fl_Region MacRegionMinusRect(Fl_Region r, int x,int y,int w,int h) +/* removes x,y,w,h rectangle from region r and returns result as a new Fl_Region + */ +{ + Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); + outr->rects = (CGRect*)malloc(4 * r->count * sizeof(CGRect)); + outr->count = 0; + CGRect rect = CGRectMake(x,y,w - 1,h - 1); + for( int i = 0; i < r->count; i++) { + CGRect A = r->rects[i]; + CGRect test = CGRectIntersection(A, rect); + if(CGRectIsEmpty(test)) { + outr->rects[(outr->count)++] = A; + } + else { + const CGFloat verylarge = 100000.; + CGRect side = CGRectMake(0,0,rect.origin.x,verylarge);//W side + test = CGRectIntersection(A, side); + if( ! CGRectIsEmpty(test)) { + outr->rects[(outr->count)++] = test; + } + side = CGRectMake(0,rect.origin.y + rect.size.height,verylarge,verylarge);//N side + test = CGRectIntersection(A, side); + if( ! CGRectIsEmpty(test)) { + outr->rects[(outr->count)++] = test; + } + side = CGRectMake(rect.origin.x + rect.size.width, 0, verylarge, verylarge);//E side + test = CGRectIntersection(A, side); + if( ! CGRectIsEmpty(test)) { + outr->rects[(outr->count)++] = test; + } + side = CGRectMake(0, 0, verylarge, rect.origin.y);//S side + test = CGRectIntersection(A, side); + if( ! CGRectIsEmpty(test)) { + outr->rects[(outr->count)++] = test; + } + } + } + if(outr->count == 0) { + free(outr->rects); + free(outr); + outr = XRectangleRegion(0,0,0,0); + } + else outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); + return outr; +} + +Fl_Region MacRectRegionIntersect(Fl_Region current, int x,int y,int w, int h) +/* intersects current and x,y,w,h rectangle and returns result as a new Fl_Region + */ +{ + if(current == NULL) return XRectangleRegion(x,y,w,h); + CGRect r = CGRectMake(x, y, w - 1, h - 1); + Fl_Region outr = (Fl_Region)malloc(sizeof(*outr)); + outr->count = current->count; + outr->rects =(CGRect*)malloc(outr->count * sizeof(CGRect)); + int j = 0; + for(int i = 0; i < current->count; i++) { + CGRect test = CGRectIntersection(current->rects[i], r); + if(!CGRectIsEmpty(test)) outr->rects[j++] = test; + } + if(j) { + outr->count = j; + outr->rects = (CGRect*)realloc(outr->rects, outr->count * sizeof(CGRect)); + } + else { + XDestroyRegion(outr); + outr = XRectangleRegion(0,0,0,0); + } + return outr; +} + +void MacCollapseWindow(Window w) +{ + [(NSWindow*)w miniaturize:nil]; +} + +static NSImage *CGBitmapContextToNSImage(CGContextRef c) +//the returned NSImage is autoreleased +{ + unsigned char *pdata = (unsigned char *)CGBitmapContextGetData(c); + NSBitmapImageRep *imagerep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&pdata + pixelsWide:CGBitmapContextGetWidth(c) + pixelsHigh:CGBitmapContextGetHeight(c) + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSDeviceRGBColorSpace + bytesPerRow:CGBitmapContextGetBytesPerRow(c) + bitsPerPixel:CGBitmapContextGetBitsPerPixel(c)]; + NSImage* image = [[NSImage alloc] initWithData: [imagerep TIFFRepresentation]]; + [imagerep release]; + return [image autorelease]; +} + +static NSCursor *PrepareCursor(NSCursor *cursor, CGContextRef (*f)() ) +{ + if(cursor == nil) { + CGContextRef c = f(); + NSImage *image = CGBitmapContextToNSImage(c); + fl_delete_offscreen( (Fl_Offscreen)c ); + NSPoint pt = {[image size].width/2, [image size].height/2}; + cursor = [[NSCursor alloc] initWithImage:image hotSpot:pt]; + } + return cursor; +} + +void *MACSetCursor(Fl_Cursor c) +{ + NSCursor *icrsr; + switch (c) { + case FL_CURSOR_CROSS: icrsr = [NSCursor crosshairCursor]; break; + case FL_CURSOR_WAIT: + static NSCursor *watch = nil; + watch = PrepareCursor(watch, CreateWatchImage); + icrsr = watch; + break; + case FL_CURSOR_INSERT: icrsr = [NSCursor IBeamCursor]; break; + case FL_CURSOR_N: icrsr = [NSCursor resizeUpCursor]; break; + case FL_CURSOR_S: icrsr = [NSCursor resizeDownCursor]; break; + case FL_CURSOR_NS: icrsr = [NSCursor resizeUpDownCursor]; break; + case FL_CURSOR_HELP: + static NSCursor *help = nil; + help = PrepareCursor(help, CreateHelpImage); + icrsr = help; + break; + case FL_CURSOR_HAND: icrsr = [NSCursor pointingHandCursor]; break; + case FL_CURSOR_MOVE: icrsr = [NSCursor openHandCursor]; break; + case FL_CURSOR_NE: + case FL_CURSOR_SW: + case FL_CURSOR_NESW: + static NSCursor *nesw = nil; + nesw = PrepareCursor(nesw, CreateNESWImage); + icrsr = nesw; + break; + case FL_CURSOR_E: icrsr = [NSCursor resizeRightCursor]; break; + case FL_CURSOR_W: icrsr = [NSCursor resizeLeftCursor]; break; + case FL_CURSOR_WE: icrsr = [NSCursor resizeLeftRightCursor]; break; + case FL_CURSOR_SE: + case FL_CURSOR_NW: + case FL_CURSOR_NWSE: + static NSCursor *nwse = nil; + nwse = PrepareCursor(nwse, CreateNWSEImage); + icrsr = nwse; + break; + case FL_CURSOR_NONE: + static NSCursor *none = nil; + none = PrepareCursor(none, CreateNoneImage); + icrsr = none; + break; + case FL_CURSOR_ARROW: + case FL_CURSOR_DEFAULT: + default: icrsr = [NSCursor arrowCursor]; + break; + } + [icrsr set]; + return icrsr; +} + +int MACscreen_init(XRectangle screens[]) +{ + NSAutoreleasePool *localPool; + localPool = [[NSAutoreleasePool alloc] init]; + NSArray *a = [NSScreen screens]; + int count = (int)[a count]; + NSRect r; + int i, num_screens = 0; + for( i = 0; i < count; i++) { + r = [[a objectAtIndex:i] frame]; + screens[num_screens].x = r.origin.x; + screens[num_screens].y = r.size.height - (r.origin.y + r.size.height); + screens[num_screens].width = r.size.width; + screens[num_screens].height = r.size.height; + num_screens ++; + if (num_screens >= 16) break; + } + [localPool release]; + return num_screens; +} + +static NSMenu *appleMenu; +static void createAppleMenu(void) +{ + static BOOL donethat = NO; + if(donethat) return; + donethat = YES; + NSMenu *mainmenu, *services; + NSMenuItem *menuItem; + NSString *title; + CFStringRef nsappname; + + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + CopyProcessName(&psn, &nsappname); + appleMenu = [[NSMenu alloc] initWithTitle:@""]; + /* Add menu items */ + title = [@"About " stringByAppendingString:(NSString*)nsappname]; + [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; + [appleMenu addItem:[NSMenuItem separatorItem]]; + // Services Menu + services = [[[NSMenu alloc] init] autorelease]; + [appleMenu addItemWithTitle:@"Services" action:nil keyEquivalent:@""]; + [appleMenu setSubmenu: services forItem: [appleMenu itemWithTitle: @"Services"]]; + // Hide AppName + title = [@"Hide " stringByAppendingString:(NSString*)nsappname]; + [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + // Hide Others + menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) + keyEquivalent:@"h"]; + [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; + // Show All + [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + [appleMenu addItem:[NSMenuItem separatorItem]]; + // Quit AppName + title = [@"Quit " stringByAppendingString:(NSString*)nsappname]; + [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; + /* Put menu into the menubar */ + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:appleMenu]; + mainmenu = [[NSMenu alloc] initWithTitle:@""]; + [mainmenu addItem:menuItem]; + if(MACsystemVersion < 0x1060) { + // [NSApp setAppleMenu:appleMenu]; + // to avoid compiler warning raised by use of undocumented setAppleMenu : + [NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu]; + } + [NSApp setServicesMenu:services]; + [NSApp setMainMenu:mainmenu]; + CFRelease(nsappname); + [services release]; + [mainmenu release]; + [appleMenu release]; + [menuItem release]; +} + +@interface FLMenuItem : NSMenuItem { + Fl_Callback *cb; + void *data; +} +- (FLMenuItem*) putData:(Fl_Callback*)callback pter:(void*)d; +- (void) doCallback:(id)obj; +- (void*)getItemData; +@end +@implementation FLMenuItem +- (FLMenuItem*) putData:(Fl_Callback*)callback pter:(void*)d +{ + cb = callback; + data = d; + return self; +} +- (void) doCallback:(id)obj +{ + cb((Fl_Widget*)obj, data); +} +- (void*)getItemData +{ + return data; +} +@end + +void *MACmainMenu(void) +{ + return (void *)[NSApp mainMenu]; +} + +void *MACcreateMenu(const char *name) +// to create a new top-level menu in the apple menu bar +// returns the created menu (NSMenu*) +{ + fl_open_display(); + NSMenu *mymenu; + NSMenuItem *menuItem; + CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + mymenu = [[NSMenu alloc] initWithTitle:(NSString*)cfname]; + CFRelease(cfname); + // Put menu into the menubar + menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; + [menuItem setSubmenu:mymenu]; + [[NSApp mainMenu] addItem:menuItem]; + [menuItem release]; + [mymenu setAutoenablesItems:NO]; + [mymenu release]; + return (void *)mymenu; +} + +void MACsetAboutMenu( Fl_Callback *cb, void *data ) +// attaches a callback to the "About myprog" item of the application menu +{ + fl_open_display(); + CFStringRef cfname = CFStringCreateCopy(NULL, (CFStringRef)[[appleMenu itemAtIndex:0] title]); + [appleMenu removeItemAtIndex:0]; + FLMenuItem *item = [[FLMenuItem alloc] autorelease]; + [item initWithTitle:(NSString*)cfname action:@selector(doCallback:) keyEquivalent:@""]; + [item putData:cb pter:data]; + [appleMenu insertItem:item atIndex:0]; + CFRelease(cfname); + [item setTarget:item]; +} + +void *MACMenuOrItemOperation(const char *operation, ...) +/* these operations apply to menus, submenus, or menu items + */ +{ + NSMenu *menu; + NSMenuItem *item; + int value; + void *pter; + void *retval = NULL; + va_list ap; + va_start(ap, operation); + + if(strcmp(operation, "itemAtIndex") == 0) {//arguments: NSMenu*, int. Returns the item + menu = va_arg(ap, NSMenu*); + value = va_arg(ap, int); + retval = (void *)[menu itemAtIndex:value]; + } + else if(strcmp(operation, "setKeyEquivalent") == 0) {//arguments: NSMenuItem*, int + item = va_arg(ap, NSMenuItem*); + value = va_arg(ap, int); + NSString *equiv = [[NSString alloc] initWithBytes:&value length:1 encoding:NSASCIIStringEncoding]; + [item setKeyEquivalent:equiv]; + [equiv release]; + } + else if(strcmp(operation, "setKeyEquivalentModifierMask") == 0) {//arguments: NSMenuItem*, int + item = va_arg(ap, NSMenuItem*); + value = va_arg(ap, int); + NSUInteger macMod = 0; + if ( value & FL_META ) macMod = NSCommandKeyMask; + if ( value & FL_SHIFT || isupper(value) ) macMod |= NSShiftKeyMask; + if ( value & FL_ALT ) macMod |= NSAlternateKeyMask; + if ( value & FL_CTRL ) macMod |= NSControlKeyMask; + [item setKeyEquivalentModifierMask:macMod]; + } + else if(strcmp(operation, "setState") == 0) {//arguments: NSMenuItem*, int + item = va_arg(ap, NSMenuItem*); + value = va_arg(ap, int); + [item setState:(value ? NSOnState : NSOffState)]; + } + else if(strcmp(operation, "initWithTitle") == 0) {//arguments: const char*title. Returns the newly created menu + //creates a new (sub)menu + pter = va_arg(ap, void *); + CFStringRef title = CFStringCreateWithCString(NULL, (const char *)pter, kCFStringEncodingUTF8); + NSMenu *menu = [[NSMenu alloc] initWithTitle:(NSString*)title]; + CFRelease(title); + [menu setAutoenablesItems:NO]; + retval = (void *)menu; + [menu autorelease]; + } + else if(strcmp(operation, "numberOfItems") == 0) {//arguments: NSMenu *menu, int *pcount + //upon return, *pcount is set to menu's item count + menu = va_arg(ap, NSMenu*); + pter = va_arg(ap, void *); + *(int*)pter = [menu numberOfItems]; + } + else if(strcmp(operation, "setSubmenu") == 0) {//arguments: NSMenuItem *item, NSMenu *menu + //sets 'menu' as submenu attached to 'item' + item = va_arg(ap, NSMenuItem*); + menu = va_arg(ap, NSMenu*); + [item setSubmenu:menu]; + } + else if(strcmp(operation, "setEnabled") == 0) {//arguments: NSMenuItem*, int + item = va_arg(ap, NSMenuItem*); + value = va_arg(ap, int); + [item setEnabled:(value ? YES : NO)]; + } + else if(strcmp(operation, "addSeparatorItem") == 0) {//arguments: NSMenu* + menu = va_arg(ap, NSMenu*); + [menu addItem:[NSMenuItem separatorItem]]; + } + else if(strcmp(operation, "setTitle") == 0) {//arguments: NSMenuItem*, const char * + item = va_arg(ap, NSMenuItem*); + pter = va_arg(ap, void *); + CFStringRef title = CFStringCreateWithCString(NULL, (const char *)pter, kCFStringEncodingUTF8); + [item setTitle:(NSString*)title]; + CFRelease(title); + } + else if(strcmp(operation, "removeItem") == 0) {//arguments: NSMenu*, int + menu = va_arg(ap, NSMenu*); + value = va_arg(ap, int); + [menu removeItem:[menu itemAtIndex:value]]; + } + else if(strcmp(operation, "getItemData") == 0) {//arguments: NSMenu*, int. Returns the item's data + //items can have a callback and a data pointer attached to them. This returns the data pointer + menu = va_arg(ap, NSMenu*); + value = va_arg(ap, int); + retval = [(FLMenuItem *)[menu itemAtIndex:value] getItemData]; + } + else if(strcmp(operation, "addNewItem") == 0) { + //arguments: NSMenu *menu, const char *name, Fl_Callback *cb, void *data, int *prank + //creates a new menu item named 'name' at the end of 'menu' + //attaches callback 'cb' and data pointer 'data' to it + //upon return, puts the rank of the new item in *prank unless prank is NULL + menu = va_arg(ap, NSMenu*); + const char *name = va_arg(ap, const char*); + Fl_Callback *cb = va_arg(ap, Fl_Callback*); + pter = va_arg(ap, void *); + int *prank = va_arg(ap, int*); + CFStringRef cfname = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + FLMenuItem *item = [FLMenuItem alloc]; + [item initWithTitle:(NSString*)cfname action:@selector(doCallback:) keyEquivalent:@""]; + [item putData:cb pter:pter]; + [menu addItem:item]; + CFRelease(cfname); + [item setTarget:item]; + if(prank != NULL) *prank = [menu indexOfItem:item]; + [item release]; + } + va_end(ap); + return retval; +} + +void MACsetkeywindow(void *nsw) +{ + [(NSWindow*)nsw makeKeyAndOrderFront:nil]; +} + +int MACpreparedrag(void) +{ + CFDataRef text = CFDataCreate(kCFAllocatorDefault, (UInt8*)fl_selection_buffer[0], fl_selection_length[0]); + if (text==NULL) return false; + NSAutoreleasePool *localPool; + localPool = [[NSAutoreleasePool alloc] init]; + NSPasteboard *mypasteboard = [NSPasteboard pasteboardWithName:NSDragPboard]; + [mypasteboard declareTypes:[NSArray arrayWithObjects:@"public.utf8-plain-text", nil] owner:nil]; + [mypasteboard setData:(NSData*)text forType:@"public.utf8-plain-text"]; + CFRelease(text); + Fl_Widget *w = Fl::pushed(); + Fl_Window *win = w->window(); + while(win->window()) win = win->window(); + NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView]; + NSEvent *theEvent = [NSApp currentEvent]; + + char *p, *q; + int width = 0, height, w2; + fl_font(FL_HELVETICA, 10); + fl_selection_buffer[0][ fl_selection_length[0] ] = 0; + p = fl_selection_buffer[0]; + int nl = 0; + while((q=strchr(p, '\n')) != NULL) { + nl++; + w2 = fl_width(p, q - p); + if(w2 > width) width = w2; + p = q + 1; + } + if(fl_selection_buffer[0][ fl_selection_length[0] - 1] != '\n') { + nl++; + w2 = fl_width(p); + if(w2 > width) width = w2; + } + height = nl * fl_height() + 3; + width += 6; + Fl_Offscreen off = fl_create_offscreen_with_alpha(width, height); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,width,height); + fl_color(FL_BLACK); + p = fl_selection_buffer[0]; + int y = fl_height(); + while(TRUE) { + q = strchr(p, '\n'); + if(q) fl_draw(p, q - p, 3, y); + else { + fl_draw(p, 3, y); + break; + } + y += fl_height(); + p = q + 1; + } + fl_end_offscreen(); + NSImage* image = CGBitmapContextToNSImage( (CGContextRef)off ); + fl_delete_offscreen( off ); + static NSSize offset={0,0}; + NSPoint pt = [theEvent locationInWindow]; + pt.x -= width/2; + pt.y -= height/2; + [myview dragImage:image at:pt offset:offset + event:theEvent pasteboard:mypasteboard + source:myview slideBack:YES]; + if ( w ) + { + int old_event = Fl::e_number; + w->handle(Fl::e_number = FL_RELEASE); + Fl::e_number = old_event; + Fl::pushed( 0 ); + } + [localPool release]; + return true; +} + +unsigned char *MACbitmapFromRectOfWindow(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel) +//delete the returned pointer after use +{ + while(win->window()) { + x += win->x(); + y += win->y(); + win = win->window(); + } + NSView *myview = [(NSWindow*)Fl_X::i(win)->xid contentView]; + [myview lockFocus]; + CGFloat epsilon = 0; + if(MACsystemVersion >= 0x1060) epsilon = 0.001; + //The epsilon offset is absolutely necessary under 10.6. Without it, the top pixel row and + //left pixel column are not read, and bitmap is read shifted by one pixel in both directions. + //Under 10.5, we want no offset. + NSRect rect = NSMakeRect(x - epsilon, y - epsilon, w, h); + NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect]; + [myview unlockFocus]; + *bytesPerPixel = [bitmap bitsPerPixel]/8; + unsigned char *data = new unsigned char[w * h * *bytesPerPixel]; + memcpy(data, [bitmap bitmapData], w * h * *bytesPerPixel); + [bitmap release]; + return data; +} + +void imgProviderReleaseData (void *info, const void *data, size_t size) +{ + delete (unsigned char *)data; +} + +CGImageRef MAC_CGImageFromRectOfWindow(Fl_Window *win, int x, int y, int w, int h) +//CFRelease the returned CGImageRef after use +{ + int bpp; + unsigned char *bitmap = MACbitmapFromRectOfWindow(win, x, y, w, h, &bpp); + CGImageRef img; + CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); + CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, bitmap, w*h*bpp, imgProviderReleaseData); + img = CGImageCreate(w, h, 8, 8*bpp, w*bpp, lut, + bpp == 3 ? kCGImageAlphaNone : kCGImageAlphaLast, + provider, NULL, false, kCGRenderingIntentDefault); + CGColorSpaceRelease(lut); + CGDataProviderRelease(provider); + return img; +} + +void MACsetContainsGLsubwindow(Fl_Window *w) +{ + [(FLWindow*)Fl_X::i(w)->xid setContainsGLsubwindow:YES]; +} + +WindowRef MACwindowRef(Fl_Window *w) +{ + return (WindowRef)[(FLWindow*)Fl_X::i(w)->xid windowRef]; +} +#endif // FL_DOXYGEN + +// +// End of "$Id: Fl_mac.cxx 6758 2009-04-13 07:32:01Z matt $". +// diff --git a/src/Fl_compose.cxx b/src/Fl_compose.cxx index a24fa8a85..a272c055e 100644 --- a/src/Fl_compose.cxx +++ b/src/Fl_compose.cxx @@ -26,6 +26,7 @@ // #include +#include // // MRS: Uncomment the following define to get the original (pre-1.1.2) @@ -38,6 +39,15 @@ #ifdef __APPLE__ +#ifdef __APPLE_COCOA__ + +static const char* const compose_pairs = +" ! % # $ y=| & : c a <<~ - r _ * +-2 3 ' u p . , 1 o >>141234? "//00A0 ... +"`A'A^A~A:A*AAE,C`E'E^E:E`I'I^I:I-D~N`O'O^O~O:Ox O/`U'U^U:U'YTHss" //00C0 ... +"`a'a^a~a:a*aae,c`e'e^e:e`i'i^i:i-d~n`o'o^o~o:o-:o/`u'u^u:u'yth:y";//00E0 ... + +#else + static const char* const compose_pairs = ":A*A,C'E~N:O:U'a`a^a:a~a*a,c'e`e" "^e:e'i`i^i:i~n'o`o^o:o~o'u`u^u:u" @@ -48,6 +58,8 @@ static const char* const compose_pairs = "++..,,_\"%%^A^E'A:E`E'I^I:I`I'O^O" "mc`O'U^U`U||^ ~^_ u . * , ~-; v "; +#endif + #else static const char* const compose_pairs = diff --git a/src/Fl_grab.cxx b/src/Fl_grab.cxx index b16ee536f..b6d49f8a5 100644 --- a/src/Fl_grab.cxx +++ b/src/Fl_grab.cxx @@ -48,7 +48,12 @@ extern HWND fl_capture; #ifdef __APPLE__ // MacOS Carbon does not seem to have a mechanism to grab the mouse pointer -extern WindowRef fl_capture; +#ifdef __APPLE_COCOA__ +extern void MACsetkeywindow(void *nsw); +extern void *fl_capture; +#else +extern Window fl_capture; +#endif #endif void Fl::grab(Fl_Window* win) { @@ -58,8 +63,13 @@ void Fl::grab(Fl_Window* win) { SetActiveWindow(fl_capture = fl_xid(first_window())); SetCapture(fl_capture); #elif defined(__APPLE__) - fl_capture = fl_xid( first_window() ); - SetUserFocusWindow( fl_capture ); +#ifdef __APPLE_COCOA__ + fl_capture = Fl_X::i(first_window())->xid; + MACsetkeywindow(fl_capture); +#else + fl_capture = fl_xid( first_window() ); + SetUserFocusWindow( fl_capture ); +#endif #else XGrabPointer(fl_display, fl_xid(first_window()), @@ -87,7 +97,9 @@ void Fl::grab(Fl_Window* win) { ReleaseCapture(); #elif defined(__APPLE__) fl_capture = 0; +#ifndef __APPLE_COCOA__ SetUserFocusWindow( (WindowRef)kUserFocusAuto ); +#endif #else XUngrabKeyboard(fl_display, fl_event_time); XUngrabPointer(fl_display, fl_event_time); diff --git a/src/cgdebug.h b/src/cgdebug.h index ab7e9330b..bc3924f11 100644 --- a/src/cgdebug.h +++ b/src/cgdebug.h @@ -212,3 +212,4 @@ inline void dbgCGContextRestoreGState(CGContextRef context) // // End of "$Id$". // + diff --git a/src/fl_arci.cxx b/src/fl_arci.cxx index a9d25f0ed..47db7b3fc 100644 --- a/src/fl_arci.cxx +++ b/src/fl_arci.cxx @@ -86,6 +86,9 @@ void fl_arc(int x,int y,int w,int h,double a1,double a2) { #elif defined(__APPLE_QUARTZ__) a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI; float cx = x + 0.5f*w - 0.5f, cy = y + 0.5f*h - 0.5f; +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif if (w!=h) { CGContextSaveGState(fl_gc); CGContextTranslateCTM(fl_gc, cx, cy); @@ -97,6 +100,9 @@ void fl_arc(int x,int y,int w,int h,double a1,double a2) { CGContextAddArc(fl_gc, cx, cy, r, a1, a2, 1); } CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif @@ -136,6 +142,9 @@ void fl_pie(int x,int y,int w,int h,double a1,double a2) { #elif defined(__APPLE_QUARTZ__) a1 = (-a1)/180.0f*M_PI; a2 = (-a2)/180.0f*M_PI; float cx = x + 0.5f*w - 0.5f, cy = y + 0.5f*h - 0.5f; +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif if (w!=h) { CGContextSaveGState(fl_gc); CGContextTranslateCTM(fl_gc, cx, cy); @@ -151,6 +160,9 @@ void fl_pie(int x,int y,int w,int h,double a1,double a2) { CGContextClosePath(fl_gc); } CGContextFillPath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif diff --git a/src/fl_ask.cxx b/src/fl_ask.cxx index 4397d3859..4496c48bf 100644 --- a/src/fl_ask.cxx +++ b/src/fl_ask.cxx @@ -273,7 +273,8 @@ void fl_beep(int type) { switch (type) { case FL_BEEP_DEFAULT : case FL_BEEP_ERROR : - SysBeep(30); +// SysBeep(30); + AlertSoundPlay(); break; default : break; diff --git a/src/fl_cursor.cxx b/src/fl_cursor.cxx index 2cdf8d41b..14b3cbb85 100644 --- a/src/fl_cursor.cxx +++ b/src/fl_cursor.cxx @@ -134,6 +134,113 @@ void Fl_Window::cursor(Fl_Cursor c, Fl_Color c1, Fl_Color c2) { # error "Either __LITTLE_ENDIAN__ or __BIG_ENDIAN__ must be defined" #endif +#ifdef __APPLE_COCOA__ +extern void *MACSetCursor(Fl_Cursor c); +extern Fl_Offscreen fl_create_offscreen_with_alpha(int w, int h); + + +CGContextRef CreateHelpImage(void) +{ + int w = 20, h = 20; + Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,w,h); + fl_color(FL_BLACK); + fl_font(FL_COURIER_BOLD, 20); + fl_draw("?", 1, h-1); + fl_end_offscreen(); + return (CGContextRef)off; +} + +CGContextRef CreateNoneImage(void) +{ + int w = 20, h = 20; + Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,w,h); + fl_end_offscreen(); + return (CGContextRef)off; +} + +CGContextRef CreateWatchImage(void) +{ + int w, h, r = 5; + w = 2*r+6; + h = 4*r; + Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,w,h); + CGContextTranslateCTM( (CGContextRef)off, w/2, h/2); + fl_color(FL_WHITE); + fl_circle(0, 0, r+1); + fl_color(FL_BLACK); + fl_rectf(-r*0.7, -r*1.7, 1.4*r, 3.4*r); + fl_rectf(r-1, -1, 3, 3); + fl_color(FL_WHITE); + fl_pie(-r, -r, 2*r, 2*r, 0, 360); + fl_color(FL_BLACK); + fl_circle(0,0,r); + fl_xyline(0, 0, -r*.7); + fl_xyline(0, 0, 0, -r*.7); + fl_end_offscreen(); + return (CGContextRef)off; +} + +CGContextRef CreateNESWImage(void) +{ + int c = 7, r = 2*c; + int w = r, h = r; + Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,w,h); + CGContextTranslateCTM( (CGContextRef)off, 0, h); + CGContextScaleCTM( (CGContextRef)off, 1, -1); + fl_color(FL_BLACK); + fl_polygon(0, 0, c, 0, 0, c); + fl_polygon(r, r, r, r-c, r-c, r); + fl_line_style(FL_SOLID, 2, 0); + fl_line(0,1, r,r+1); + fl_line_style(FL_SOLID, 0, 0); + fl_end_offscreen(); + return (CGContextRef)off; +} + +CGContextRef CreateNWSEImage(void) +{ + int c = 7, r = 2*c; + int w = r, h = r; + Fl_Offscreen off = fl_create_offscreen_with_alpha(w, h); + fl_begin_offscreen(off); + CGContextSetRGBFillColor( (CGContextRef)off, 0,0,0,0); + fl_rectf(0,0,w,h); + CGContextTranslateCTM( (CGContextRef)off, 0, h); + CGContextScaleCTM( (CGContextRef)off, 1, -1); + fl_color(FL_BLACK); + fl_polygon(r-1, 0, r-1, c, r-1-c, 0); + fl_polygon(-1, r, c-1, r, -1, r-c); + fl_line_style(FL_SOLID, 2, 0); + fl_line(r-1,1, -1,r+1); + fl_line_style(FL_SOLID, 0, 0); + fl_end_offscreen(); + return (CGContextRef)off; +} + +void Fl_Window::cursor(Fl_Cursor c, Fl_Color, Fl_Color) { + if (c == FL_CURSOR_DEFAULT) { + c = cursor_default; + } + void *cursor = MACSetCursor( c ); + if (i) { + i->cursor = cursor; + } +} + +#else + static Cursor crsrHAND = { { E(0x0600), E(0x0900), E(0x0900), E(0x0900), E(0x09C0), E(0x0938), E(0x6926), E(0x9805), @@ -214,7 +321,7 @@ void Fl_Window::cursor(Fl_Cursor c, Fl_Color, Fl_Color) { c = cursor_default; } CursHandle icrsr = fl_default_cursor; - switch (c) { + switch (c) { case FL_CURSOR_CROSS: icrsr = GetCursor( crossCursor ); break; case FL_CURSOR_WAIT: icrsr = GetCursor( watchCursor ); break; case FL_CURSOR_INSERT: icrsr = GetCursor( iBeamCursor ); break; @@ -246,6 +353,8 @@ void Fl_Window::cursor(Fl_Cursor c, Fl_Color, Fl_Color) { } } +#endif //__APPLE_COCOA__ + #else // I like the MSWindows resize cursors, so I duplicate them here: diff --git a/src/fl_dnd_mac.cxx b/src/fl_dnd_mac.cxx index af8cf1c58..9928e4b9f 100644 --- a/src/fl_dnd_mac.cxx +++ b/src/fl_dnd_mac.cxx @@ -46,6 +46,11 @@ extern int fl_selection_length; */ int Fl::dnd() { +#ifdef __APPLE_COCOA__ + extern int MACpreparedrag(void); + return MACpreparedrag(); +#else + OSErr result; DragReference dragRef; result = NewDrag( &dragRef ); @@ -82,6 +87,7 @@ int Fl::dnd() DisposeRgn( region ); DisposeDrag( dragRef ); return true; +#endif //__APPLE_COCOA__ } diff --git a/src/fl_font_mac.cxx b/src/fl_font_mac.cxx index 3e9db0763..552a579ad 100644 --- a/src/fl_font_mac.cxx +++ b/src/fl_font_mac.cxx @@ -33,6 +33,8 @@ extern unsigned fl_utf8toUtf16(const char* src, unsigned srclen, unsigned short* // if no font has been selected yet by the user, get one. #define check_default_font() {if (!fl_fontsize) fl_font(0, 12);} +static const CGAffineTransform font_mx = { 1, 0, 0, -1, 0, 0 }; + Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { next = 0; # if HAVE_GL @@ -43,13 +45,39 @@ Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { // OpenGL needs those for its font handling q_name = strdup(name); size = Size; + minsize = maxsize = Size; +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + CFStringRef str = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + fontref = CTFontCreateWithName(str, size, NULL); + CGGlyph glyph[2]; + const UniChar A[2]={'W','.'}; + CTFontGetGlyphsForCharacters(fontref, A, glyph, 2); + CGSize advances[2]; + double w; + CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, advances, 2); + w = advances[0].width; + if( abs(advances[0].width - advances[1].width) < 1E-2 ) {//this is a fixed-width font + //slightly rescale fixed-width fonts so the character width has an integral value + CFRelease(fontref); + CGFloat fsize = size / ( w/floor(w + 0.5) ); + fontref = CTFontCreateWithName(str, fsize, NULL); + w = CTFontGetAdvancesForGlyphs(fontref, kCTFontHorizontalOrientation, glyph, NULL, 1); + } + CFRelease(str); + ascent = (short)(CTFontGetAscent(fontref) + 0.5); + descent = (short)(CTFontGetDescent(fontref) + 0.5); + q_width = w + 0.5; + } +else { +#endif +#if ! __LP64__ OSStatus err; // fill our structure with a few default values ascent = Size*3/4; descent = Size-ascent; q_width = Size*2/3; - minsize = maxsize = Size; - // now use ATS to get the actual Glyph size information + // now use ATS to get the actual Glyph size information // say that our passed-in name is encoded as UTF-8, since this works for plain ASCII names too... CFStringRef cfname = CFStringCreateWithCString(0L, name, kCFStringEncodingUTF8); ATSFontRef font = ATSFontFindFromName(cfname, kATSOptionFlagsDefault); @@ -112,6 +140,10 @@ Fl_Font_Descriptor::Fl_Font_Descriptor(const char* name, Fl_Fontsize Size) { // cause ATSU to find a suitable font to render any chars the current font can't do... ATSUSetTransientFontMatching (layout, true); # endif +#endif//__LP64__ +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif } Fl_Font_Descriptor* fl_fontsize = 0L; @@ -132,8 +164,12 @@ Fl_Font_Descriptor::~Fl_Font_Descriptor() { #endif */ if (this == fl_fontsize) fl_fontsize = 0; - ATSUDisposeTextLayout(layout); - ATSUDisposeStyle(style); +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + if(CTFontCreateWithName != NULL) CFRelease(fontref); +#else + /* ATSUDisposeTextLayout(layout); + ATSUDisposeStyle(style); */ +#endif } //////////////////////////////////////////////////////////////// @@ -235,7 +271,25 @@ double fl_width(const UniChar* txt, int n) { if (!fl_fontsize) return 8*n; // user must select a font first! } - OSStatus err; +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + CTFontRef fontref = fl_fontsize->fontref; + CFStringRef str = CFStringCreateWithBytes(NULL, (const UInt8*)txt, n * sizeof(UniChar), kCFStringEncodingUTF16, false); + CFAttributedStringRef astr = CFAttributedStringCreate(NULL, str, NULL); + CFMutableAttributedStringRef mastr = CFAttributedStringCreateMutableCopy(NULL, 0, astr); + CFRelease(astr); + CFAttributedStringSetAttribute(mastr, CFRangeMake(0, CFStringGetLength(str)), kCTFontAttributeName, fontref); + CFRelease(str); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + double retval = CTLineGetTypographicBounds(ctline, NULL, NULL, NULL); + CFRelease(ctline); + return retval; + } +else { +#endif +#if ! __LP64__ + OSStatus err; Fixed bBefore, bAfter, bAscent, bDescent; ATSUTextLayout layout; ByteCount iSize; @@ -256,6 +310,10 @@ double fl_width(const UniChar* txt, int n) { // If err is OK then return length, else return 0. Or something... int len = FixedToInt(bAfter); return len; +#endif +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif } double fl_width(const char* txt, int n) { @@ -281,6 +339,30 @@ void fl_text_extents(const UniChar* txt, int n, int &dx, int &dy, int &w, int &h h = 8.0; return; } +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + CTFontRef fontref = fl_fontsize->fontref; + CFStringRef str16 = CFStringCreateWithBytes(NULL, (const UInt8*)txt, n *sizeof(UniChar), kCFStringEncodingUTF16, false); + CFAttributedStringRef astr = CFAttributedStringCreate(NULL, str16, NULL); + CFMutableAttributedStringRef mastr = CFAttributedStringCreateMutableCopy(NULL, 0, astr); + CFRelease(astr); + CFAttributedStringSetAttribute(mastr, CFRangeMake(0, CFStringGetLength(str16)), kCTFontAttributeName, fontref); + CFRelease(str16); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + CGContextSetTextPosition(fl_gc, 0, 0); + CGContextSetShouldAntialias(fl_gc, true); + CGRect rect = CTLineGetImageBounds(ctline, fl_gc); + CGContextSetShouldAntialias(fl_gc, false); + CFRelease(ctline); + dx = floor(rect.origin.x + 0.5); + dy = floor(- rect.origin.y - rect.size.height + 0.5); + w = rect.size.width + 0.5; + h = rect.size.height + 0.5; + } +else { +#endif +#if ! __LP64__ OSStatus err; ATSUTextLayout layout; ByteCount iSize; @@ -304,6 +386,10 @@ void fl_text_extents(const UniChar* txt, int n, int &dx, int &dy, int &w, int &h dx = bbox.left; dy = -bbox.bottom; //printf("r: %d l: %d t: %d b: %d w: %d h: %d\n", bbox.right, bbox.left, bbox.top, bbox.bottom, w, h); +#endif +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif return; } // fl_text_extents @@ -320,13 +406,76 @@ void fl_draw(const char* str, int n, int x, int y) { fl_draw(str, n, (float)x-0.0f, (float)y-0.5f); } + +#if defined(__APPLE_COCOA__) +static unsigned fl_cmap[256] = { +#include "fl_cmap.h" // this is a file produced by "cmap.cxx": +}; +CGColorRef flcolortocgcolor(Fl_Color i) +{ + int index; + uchar r, g, b; + if (i & 0xFFFFFF00) { + // translate rgb colors into color index + r = i>>24; + g = i>>16; + b = i>> 8; + } else { + // translate index into rgb: + index = i; + unsigned c = fl_cmap[i]; + r = c>>24; + g = c>>16; + b = c>> 8; + } + CGFloat components[4] = {r/255.0f, g/255.0f, b/255.0f, 1.}; +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4 + return CGColorCreate(CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB), components); +#else + return CGColorCreate(CGColorSpaceCreateWithName(kCGColorSpaceUserRGB), components); +#endif +} +#endif + void fl_draw(const char *str, int n, float x, float y) { - OSStatus err; - // convert to UTF-16 first - UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); // avoid a crash if no font has been selected by user yet ! check_default_font(); + // convert to UTF-16 first + UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + CFStringRef keys[2]; + CFTypeRef values[2]; + CFStringRef str16 = CFStringCreateWithBytes(NULL, (const UInt8*)uniStr, n * sizeof(UniChar), kCFStringEncodingUTF16, false); + CGColorRef color = flcolortocgcolor(fl_color()); + keys[0] = kCTFontAttributeName; + keys[1] = kCTForegroundColorAttributeName; + values[0] = fl_fontsize->fontref; + values[1] = color; + CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, + (const void**)&keys, + (const void**)&values, + 2, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFAttributedStringRef mastr = CFAttributedStringCreate(kCFAllocatorDefault, str16, attributes); + CFRelease(str16); + CFRelease(attributes); + CFRelease(color); + CTLineRef ctline = CTLineCreateWithAttributedString(mastr); + CFRelease(mastr); + CGContextSetTextMatrix(fl_gc, font_mx); + CGContextSetTextPosition(fl_gc, x, y); + CGContextSetShouldAntialias(fl_gc, true); + CTLineDraw(ctline, fl_gc); + CGContextSetShouldAntialias(fl_gc, false); + CFRelease(ctline); + } +else { +#endif +#if ! __LP64__ + OSStatus err; // now collect our ATSU resources ATSUTextLayout layout = fl_fontsize->layout; @@ -337,10 +486,25 @@ void fl_draw(const char *str, int n, float x, float y) { err = ATSUSetTextPointerLocation(layout, uniStr, kATSUFromTextBeginning, n, n); err = ATSUDrawText(layout, kATSUFromTextBeginning, n, FloatToFixed(x), FloatToFixed(y)); +#endif +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif } void fl_draw(int angle, const char *str, int n, int x, int y) { - OSStatus err; +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + CGContextSaveGState(fl_gc); + CGContextTranslateCTM(fl_gc, x, y); + CGContextRotateCTM(fl_gc, - angle*(M_PI/180) ); + fl_draw(str, n, (float)0., (float)0.); + CGContextRestoreGState(fl_gc); + } +else { +#endif +#if ! __LP64__ + OSStatus err; // convert to UTF-16 first UniChar *uniStr = mac_Utf8_to_Utf16(str, n, &n); @@ -360,10 +524,21 @@ void fl_draw(int angle, const char *str, int n, int x, int y) { //restore layout baseline ang = IntToFixed(0); ATSUSetLayoutControls(layout, 2, iTag, iSize, aAttr); +#endif +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif } void fl_rtl_draw(const char* c, int n, int x, int y) { -// I guess with ATSU the thing to do is force the layout mode to RTL and let ATSU draw the text... +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithName != NULL) { + fl_draw(c, n, x - fl_width(c, n), y); //to check; + } +else { +#endif +#if ! __LP64__ + // I guess with ATSU the thing to do is force the layout mode to RTL and let ATSU draw the text... double offs = fl_width(c, n); OSStatus err; // convert to UTF-16 first @@ -379,6 +554,10 @@ void fl_rtl_draw(const char* c, int n, int x, int y) { err = ATSUSetTextPointerLocation(layout, uniStr, kATSUFromTextBeginning, n, n); err = ATSUDrawText(layout, kATSUFromTextBeginning, n, FloatToFixed(x-offs), FloatToFixed(y)); +#endif +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } +#endif } // diff --git a/src/fl_line_style.cxx b/src/fl_line_style.cxx index 287a2eaf7..bf299bbc7 100644 --- a/src/fl_line_style.cxx +++ b/src/fl_line_style.cxx @@ -40,7 +40,7 @@ float fl_quartz_line_width_ = 1.0f; static enum CGLineCap fl_quartz_line_cap_ = kCGLineCapButt; static enum CGLineJoin fl_quartz_line_join_ = kCGLineJoinMiter; -static float *fl_quartz_line_pattern = 0; +static CGFloat *fl_quartz_line_pattern = 0; static int fl_quartz_line_pattern_size = 0; void fl_quartz_restore_line_style_() { CGContextSetLineWidth(fl_gc, fl_quartz_line_width_); @@ -145,9 +145,9 @@ void fl_line_style(int style, int width, char* dashes) { fl_quartz_line_cap_ = Cap[(style>>8)&3]; fl_quartz_line_join_ = Join[(style>>12)&3]; char *d = dashes; - static float pattern[16]; + static CGFloat pattern[16]; if (d && *d) { - float *p = pattern; + CGFloat *p = pattern; while (*d) { *p++ = (float)*d++; } fl_quartz_line_pattern = pattern; fl_quartz_line_pattern_size = d-dashes; @@ -162,7 +162,7 @@ void fl_line_style(int style, int width, char* dashes) { dash = char(3*width); dot = gap = char(width); } - float *p = pattern; + CGFloat *p = pattern; switch (style & 0xff) { case FL_DASH: *p++ = dash; *p++ = gap; break; case FL_DOT: *p++ = dot; *p++ = gap; break; @@ -172,7 +172,8 @@ void fl_line_style(int style, int width, char* dashes) { fl_quartz_line_pattern_size = p-pattern; fl_quartz_line_pattern = pattern; } else { - fl_quartz_line_pattern = 0; fl_quartz_line_pattern_size = 0; + fl_quartz_line_pattern = 0; + fl_quartz_line_pattern_size = 0; } fl_quartz_restore_line_style_(); #else diff --git a/src/fl_read_image_mac.cxx b/src/fl_read_image_mac.cxx index 3df921173..08e4361f7 100644 --- a/src/fl_read_image_mac.cxx +++ b/src/fl_read_image_mac.cxx @@ -26,9 +26,9 @@ // #include - -// warning: this function is only implemented in Quickdraw. The function -// below may not work If FLTK is compiled with Quartz enabled +#ifdef __APPLE_COCOA__ +extern unsigned char *MACbitmapFromRectOfWindow(Fl_Window *win, int x, int y, int w, int h, int *bytesPerPixel); +#endif // // 'fl_read_image()' - Read an image from the current window. @@ -41,6 +41,29 @@ fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate int w, // I - Width of area to read int h, // I - Height of area to read int alpha) { // I - Alpha value for image (0 for none) +#if defined(__APPLE_COCOA__) + Fl_Window *window = Fl_Window::current(); + while(window->window()) window = window->window(); + int delta; + uchar *base = MACbitmapFromRectOfWindow(window,x,y,w,h,&delta); + int rowBytes = delta*w; + // Allocate the image data array as needed... + int d = alpha ? 4 : 3; + if (!p) p = new uchar[w * h * d]; + // Initialize the default colors/alpha in the whole image... + memset(p, alpha, w * h * d); + // Copy the image from the off-screen buffer to the memory buffer. + int idx, idy; // Current X & Y in image + uchar *pdst, *psrc; + for (idy = 0, pdst = p; idy < h; idy ++) { + for (idx = 0, psrc = base + idy * rowBytes; idx < w; idx ++, psrc += delta, pdst += d) { +/*R*/ pdst[0] = psrc[0]; +/*G*/ pdst[1] = psrc[1]; +/*B*/ pdst[2] = psrc[2]; + } + } +delete base; +#else Rect src, // Source rectangle dst; // Destination rectangle GWorldPtr osbuffer; // Temporary off-screen buffer for copy @@ -128,7 +151,8 @@ fl_read_image(uchar *p, // I - Pixel buffer or NULL to allocate DisposeGWorld(osbuffer); SetPort(srcPort); - return p; +#endif +return p; } diff --git a/src/fl_rect.cxx b/src/fl_rect.cxx index fb7fe0f05..0709f2ff9 100644 --- a/src/fl_rect.cxx +++ b/src/fl_rect.cxx @@ -59,10 +59,18 @@ void fl_rect(int x, int y, int w, int h) { LineTo(fl_gc, x, y+h-1); LineTo(fl_gc, x, y); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGRect rect = CGRectMake(x, y, w-1, h-1); CGContextStrokeRect(fl_gc, rect); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -81,10 +89,18 @@ void fl_rectf(int x, int y, int w, int h) { rect.right = x + w; rect.bottom = y + h; FillRect(fl_gc, &rect, fl_brush()); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGRect rect = CGRectMake(x, y, w-1, h-1); CGContextFillRect(fl_gc, rect); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -99,11 +115,19 @@ void fl_xyline(int x, int y, int x1) { #elif defined(WIN32) MoveToEx(fl_gc, x, y, 0L); LineTo(fl_gc, x1+1, y); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -125,12 +149,20 @@ void fl_xyline(int x, int y, int x1, int y2) { LineTo(fl_gc, x1, y); LineTo(fl_gc, x1, y2); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y); CGContextAddLineToPoint(fl_gc, x1, y2); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else #error unsupported platform #endif @@ -155,13 +187,21 @@ void fl_xyline(int x, int y, int x1, int y2, int x3) { LineTo(fl_gc, x1, y2); LineTo(fl_gc, x3, y2); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y); CGContextAddLineToPoint(fl_gc, x1, y2); CGContextAddLineToPoint(fl_gc, x3, y2); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -178,11 +218,19 @@ void fl_yxline(int x, int y, int y1) { else y1++; MoveToEx(fl_gc, x, y, 0L); LineTo(fl_gc, x, y1); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x, y1); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -204,12 +252,20 @@ void fl_yxline(int x, int y, int y1, int x2) { LineTo(fl_gc, x, y1); LineTo(fl_gc, x2, y1); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x, y1); CGContextAddLineToPoint(fl_gc, x2, y1); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -234,13 +290,21 @@ void fl_yxline(int x, int y, int y1, int x2, int y3) { LineTo(fl_gc, x2, y1); LineTo(fl_gc, x2, y3); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x, y1); CGContextAddLineToPoint(fl_gc, x2, y1); CGContextAddLineToPoint(fl_gc, x2, y3); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -259,11 +323,19 @@ void fl_line(int x, int y, int x1, int y1) { // functions will not draw the last point ("it's a feature!"...) SetPixel(fl_gc, x1, y1, fl_RGB()); #elif defined(__APPLE_QUARTZ__) - if (fl_quartz_line_width_==1.0f ) CGContextSetShouldAntialias(fl_gc, false); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else + if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -287,12 +359,20 @@ void fl_line(int x, int y, int x1, int y1, int x2, int y2) { // functions will not draw the last point ("it's a feature!"...) SetPixel(fl_gc, x2, y2, fl_RGB()); #elif defined(__APPLE_QUARTZ__) - if (fl_quartz_line_width_==1.0f ) CGContextSetShouldAntialias(fl_gc, false); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else + if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextAddLineToPoint(fl_gc, x2, y2); CGContextStrokePath(fl_gc); - if (fl_quartz_line_width_==1.0f ) CGContextSetShouldAntialias(fl_gc, true); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else + if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -315,11 +395,17 @@ void fl_loop(int x, int y, int x1, int y1, int x2, int y2) { LineTo(fl_gc, x2, y2); LineTo(fl_gc, x, y); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextAddLineToPoint(fl_gc, x2, y2); CGContextClosePath(fl_gc); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif @@ -344,12 +430,18 @@ void fl_loop(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { LineTo(fl_gc, x3, y3); LineTo(fl_gc, x, y); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextAddLineToPoint(fl_gc, x2, y2); CGContextAddLineToPoint(fl_gc, x3, y3); CGContextClosePath(fl_gc); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif @@ -371,11 +463,17 @@ void fl_polygon(int x, int y, int x1, int y1, int x2, int y2) { SelectObject(fl_gc, fl_brush()); Polygon(fl_gc, p, 3); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextAddLineToPoint(fl_gc, x2, y2); CGContextClosePath(fl_gc); CGContextFillPath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif @@ -398,12 +496,18 @@ void fl_polygon(int x, int y, int x1, int y1, int x2, int y2, int x3, int y3) { SelectObject(fl_gc, fl_brush()); Polygon(fl_gc, p, 4); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif CGContextMoveToPoint(fl_gc, x, y); CGContextAddLineToPoint(fl_gc, x1, y1); CGContextAddLineToPoint(fl_gc, x2, y2); CGContextAddLineToPoint(fl_gc, x3, y3); CGContextClosePath(fl_gc); CGContextFillPath(fl_gc); +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, false); +#endif #else # error unsupported platform #endif @@ -418,11 +522,19 @@ void fl_point(int x, int y) { #elif defined(WIN32) SetPixel(fl_gc, x, y, fl_RGB()); #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif CGContextMoveToPoint(fl_gc, x-.5, y); // Quartz needs a line that is one pixel long, or it will not draw anything CGContextAddLineToPoint(fl_gc, x+.5, y); CGContextStrokePath(fl_gc); +#ifdef __APPLE_COCOA__ + if (fl_quartz_line_width_ > 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -467,7 +579,17 @@ void fl_restore_clip() { #elif defined(__APPLE_QUARTZ__) if ( fl_window ) // clipping for a true window { - GrafPtr port = GetWindowPort( fl_window ); +#ifdef __APPLE_COCOA__ + Fl_X::q_clear_clipping(); + Fl_X::q_fill_context();//flip coords and translate if subwindow + //apply window's clip + CGContextClipToRects(fl_gc, fl_window_region->rects, fl_window_region->count ); + //apply additional program clip + if(r) { + CGContextClipToRects(fl_gc, r->rects, r->count); + } +#else + GrafPtr port = GetWindowPort( fl_window ); if ( port ) { RgnHandle portClip = NewRgn(); CopyRgn( fl_window_region, portClip ); // changed @@ -479,16 +601,22 @@ void fl_restore_clip() { Fl_X::q_fill_context(); DisposeRgn( portClip ); } +#endif } else if (fl_gc) { // clipping for an offscreen drawing world (CGBitmap) - Rect portRect; - portRect.top = 0; - portRect.left = 0; - portRect.bottom = CGBitmapContextGetHeight(fl_gc); - portRect.right = CGBitmapContextGetWidth(fl_gc); - Fl_X::q_clear_clipping(); - if (r) - ClipCGContextToRegion(fl_gc, &portRect, r); - Fl_X::q_fill_context(); + Rect portRect; + portRect.top = 0; + portRect.left = 0; + portRect.bottom = CGBitmapContextGetHeight(fl_gc); + portRect.right = CGBitmapContextGetWidth(fl_gc); + Fl_X::q_clear_clipping(); + if (r) { +#ifdef __APPLE_COCOA__ + CGContextClipToRects(fl_gc, r->rects, r->count); +#else + ClipCGContextToRegion(fl_gc, &portRect, r); +#endif + } + Fl_X::q_fill_context(); } #else # error unsupported platform @@ -534,19 +662,33 @@ void fl_push_clip(int x, int y, int w, int h) { #elif defined(WIN32) CombineRgn(r,r,current,RGN_AND); #elif defined(__APPLE_QUARTZ__) - SectRgn(r, current, r); +#ifdef __APPLE_COCOA__ + XDestroyRegion(r); + r = MacRectRegionIntersect(current, x,y,w,h); +#else + SectRgn(r, current, r); +#endif #else # error unsupported platform #endif } +#if defined(__APPLE_QUARTZ__) + else { + r = XRectangleRegion(x,y,w,h); + } +#endif } else { // make empty clip region: #if defined(USE_X11) r = XCreateRegion(); #elif defined(WIN32) r = CreateRectRgn(0,0,0,0); #elif defined(__APPLE_QUARTZ__) - r = NewRgn(); - SetEmptyRgn(r); +#ifdef __APPLE_COCOA__ + r = NULL; +#else + r = NewRgn(); + SetEmptyRgn(r); +#endif #else # error unsupported platform #endif @@ -604,9 +746,18 @@ int fl_not_clipped(int x, int y, int w, int h) { return RectInRegion(r,&rect); #elif defined(__APPLE_QUARTZ__) if (!r) return 1; +#ifdef __APPLE_COCOA__ + CGRect arg = CGRectMake(x,y,w - 1,h - 1); + for(int i = 0; i < r->count; i++) { + CGRect test = CGRectIntersection(r->rects[i], arg); + if( ! CGRectIsEmpty(test)) return 1; + } + return 0; +#else Rect rect; rect.left = x; rect.top = y; rect.right = x+w; rect.bottom = y+h; return RectInRgn(&rect, r); +#endif #else # error unsupported platform #endif @@ -673,6 +824,24 @@ int fl_clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ DeleteObject(rr); return ret; #elif defined(__APPLE_QUARTZ__) +#ifdef __APPLE_COCOA__ + CGRect arg = CGRectMake(x,y,w - 1,h - 1); + CGRect u = CGRectMake(0,0,0,0); + CGRect test; + for(int i = 0; i < r->count; i++) { + test = CGRectIntersection(r->rects[i], arg); + if( ! CGRectIsEmpty(test) ) { + if(CGRectIsEmpty(u)) u = test; + else u = CGRectUnion(u, test); + } + } + X = u.origin.x; + Y = u.origin.y; + W = u.size.width; + H = u.size.height; + if(CGRectIsEmpty(u)) W = H = 0; + return ! CGRectEqualToRect(arg, u); +#else RgnHandle rr = NewRgn(); SetRectRgn( rr, x, y, x+w, y+h ); SectRgn( r, rr, rr ); @@ -685,6 +854,7 @@ int fl_clip_box(int x, int y, int w, int h, int& X, int& Y, int& W, int& H){ if ( H==0 ) return 2; if ( h==H && w==W ) return 0; return 0; +#endif #else # error unsupported platform #endif diff --git a/src/fl_scroll_area.cxx b/src/fl_scroll_area.cxx index 40609bc51..ff07ef3b4 100644 --- a/src/fl_scroll_area.cxx +++ b/src/fl_scroll_area.cxx @@ -151,6 +151,16 @@ void fl_scroll(int X, int Y, int W, int H, int dx, int dy, BitBlt(fl_gc, dest_x, dest_y, src_w, src_h, fl_gc, src_x, src_y,SRCCOPY); #elif defined(__APPLE_QUARTZ__) + +#if defined(__APPLE_COCOA__) + extern CGImageRef MAC_CGImageFromRectOfWindow(Fl_Window*, int x, int y, int w, int h); + CGImageRef img = MAC_CGImageFromRectOfWindow(Fl_Window::current(), src_x, src_y, src_w, src_h); + CGRect rect = { { dest_x, dest_y }, { src_w, src_h } }; + Fl_X::q_begin_image(rect, 0, 0, src_w, src_h); + CGContextDrawImage(fl_gc, rect, img); + Fl_X::q_end_image(); + CFRelease(img); +#else // warning: there does not seem to be an equivalent to this function in Quartz // ScrollWindowRect is a QuickDraw function and won't work here. // Since on OS X all windows are fully double buffered, we need not @@ -161,6 +171,8 @@ void fl_scroll(int X, int Y, int W, int H, int dx, int dy, static RGBColor fg = { 0x0000, 0x0000, 0x0000 }; RGBForeColor( &fg ); CopyBits( GetPortBitMapForCopyBits( GetWindowPort(fl_window) ), GetPortBitMapForCopyBits( GetWindowPort(fl_window) ), &src, &dst, srcCopy, 0L); +#endif + #else # error unsupported platform #endif diff --git a/src/fl_set_fonts_mac.cxx b/src/fl_set_fonts_mac.cxx index bb54f5631..84715f1b2 100644 --- a/src/fl_set_fonts_mac.cxx +++ b/src/fl_set_fonts_mac.cxx @@ -60,6 +60,33 @@ static int fl_free_font = FL_FREE_FONT; Fl_Font Fl::set_fonts(const char* xstarname) { #pragma unused ( xstarname ) +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 +if(CTFontCreateWithFontDescriptor != NULL) { + int value[1] = {1}; + CFDictionaryRef dict = CFDictionaryCreate(NULL, (const void **)kCTFontCollectionRemoveDuplicatesOption, + (const void **)&value, 1, NULL, NULL); + CTFontCollectionRef fcref = CTFontCollectionCreateFromAvailableFonts(dict); + CFRelease(dict); + CFArrayRef arrayref = CTFontCollectionCreateMatchingFontDescriptors(fcref); + CFRelease(fcref); + CFIndex count = CFArrayGetCount(arrayref); + CFIndex i; + for (i = 0; i < count; i++) { + CTFontDescriptorRef fdesc = (CTFontDescriptorRef)CFArrayGetValueAtIndex(arrayref, i); + CTFontRef font = CTFontCreateWithFontDescriptor(fdesc, 0., NULL); + CFStringRef cfname = CTFontCopyPostScriptName(font); + CFRelease(font); + static char fname[100]; + CFStringGetCString(cfname, fname, sizeof(fname), kCFStringEncodingUTF8); + CFRelease(cfname); + Fl::set_font((Fl_Font)(fl_free_font++), strdup(fname)); + } + CFRelease(arrayref); + return (Fl_Font)fl_free_font; +} +else { +#endif +#if ! __LP64__ #if defined(OLD__APPLE_QUARTZ__) ATSFontIterator it; ATSFontIteratorCreate(kATSFontContextGlobal, 0L, 0L, kATSOptionFlagsUnRestrictedScope, &it); @@ -114,6 +141,10 @@ Fl_Font Fl::set_fonts(const char* xstarname) { } free(oFontIDs); return (Fl_Font)fl_free_font; +#endif //OLD__APPLE_QUARTZ__ +#endif //__LP64__ +#if defined(__APPLE_COCOA__) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + } #endif } diff --git a/src/fl_vertex.cxx b/src/fl_vertex.cxx index 66e5102de..36e0aaae9 100644 --- a/src/fl_vertex.cxx +++ b/src/fl_vertex.cxx @@ -229,13 +229,21 @@ void fl_end_points() { #elif defined(WIN32) for (int i=0; i 1.5f) CGContextSetShouldAntialias(fl_gc, true); +#else if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, false); +#endif for (int i=0; i 1.5f) CGContextSetShouldAntialias(fl_gc, false); +#else + if (fl_quartz_line_width_==1.0f) CGContextSetShouldAntialias(fl_gc, true); +#endif #else # error unsupported platform #endif @@ -255,10 +263,16 @@ void fl_end_line() { if (n>1) Polyline(fl_gc, p, n); #elif defined(__APPLE_QUARTZ__) if (n<=1) return; +#ifdef __APPLE_COCOA__ + CGContextSetShouldAntialias(fl_gc, true); +#endif CGContextMoveToPoint(fl_gc, p[0].x, p[0].y); for (int i=1; ilistbase+base); SelectObject(fl_gc, oldFid); # elif defined(__APPLE_QUARTZ__) +#if ! __LP64__ +//AGL is not supported for use in 64-bit applications: +//http://developer.apple.com/mac/library/documentation/Carbon/Conceptual/Carbon64BitGuide/OtherAPIChanges/OtherAPIChanges.html short font, face, size; uchar fn[256]; fn[0]=strlen(fl_fontsize->q_name); @@ -118,8 +121,9 @@ void gl_font(int fontid, int size) { face = 0; size = fl_fontsize->size; fl_fontsize->listbase = glGenLists(256); - aglUseFont(aglGetCurrentContext(), font, face, + aglUseFont(aglGetCurrentContext(), font, face, size, 0, 256, fl_fontsize->listbase); +#endif # else # error unsupported platform # endif @@ -128,7 +132,9 @@ void gl_font(int fontid, int size) { } gl_fontsize = fl_fontsize; +#if !( defined(__APPLE__) && __LP64__ ) glListBase(fl_fontsize->listbase); +#endif } #ifndef __APPLE__ @@ -204,10 +210,20 @@ void gl_remove_displaylist_fonts() Draws an array of n characters of the string in the current font at the current position. */ +#if defined(__APPLE__) && __LP64__ +static void gl_draw_cocoa(const char* str, int n); +#endif + void gl_draw(const char* str, int n) { #ifdef __APPLE__ + +#if __LP64__ + gl_draw_cocoa(str, n); +#else // Should be converting the text here, as for other platforms??? glCallLists(n, GL_UNSIGNED_BYTE, str); +#endif + #else static xchar *buf = NULL; static int l = 0; @@ -236,7 +252,7 @@ void gl_draw(const char* str, int n) { } /** - Draws n charachters of the string in the current font at the given position + Draws n characters of the string in the current font at the given position */ void gl_draw(const char* str, int n, int x, int y) { glRasterPos2i(x, y); @@ -244,7 +260,7 @@ void gl_draw(const char* str, int n, int x, int y) { } /** - Draws n charachters of the string in the current font at the given position + Draws n characters of the string in the current font at the given position */ void gl_draw(const char* str, int n, float x, float y) { glRasterPos2f(x, y); @@ -351,6 +367,95 @@ void gl_draw_image(const uchar* b, int x, int y, int w, int h, int d, int ld) { glDrawPixels(w,h,d<4?GL_RGB:GL_RGBA,GL_UNSIGNED_BYTE,(const ulong*)b); } +#if defined(__APPLE__) && defined(__APPLE_COCOA__) && __LP64__ + +#include + +static void gl_draw_cocoa(const char* str, int n) +{ +//setup matrices + GLint matrixMode; + glGetIntegerv (GL_MATRIX_MODE, &matrixMode); + glMatrixMode (GL_PROJECTION); + glPushMatrix(); + glLoadIdentity (); + glMatrixMode (GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity (); + float winw = Fl_Window::current()->w(); + float winh = Fl_Window::current()->h(); + glScalef (2.0f / winw, 2.0f / winh, 1.0f); + glTranslatef (-winw / 2.0f, -winh / 2.0f, 0.0f); +//write str to a bitmap just big enough + int w = 0, h = 0; + fl_measure(str, w, h, 0); + CGColorSpaceRef lut = CGColorSpaceCreateDeviceRGB(); + void *base = calloc(4*w, h); + if(base == NULL) return; + fl_gc = CGBitmapContextCreate(base, w, h, 8, w*4, lut, kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease(lut); + fl_fontsize = gl_fontsize; + fl_draw(str, 0, h - fl_descent()); +//put this bitmap in a texture + static GLuint texName = 0; + glPushAttrib(GL_TEXTURE_BIT); + if (0 == texName) glGenTextures (1, &texName); + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, texName); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_RECTANGLE_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_RECTANGLE_EXT, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, base); + glPopAttrib(); + CGContextRelease(fl_gc); + fl_gc = NULL; + free(base); + GLfloat pos[4]; + glGetFloatv(GL_CURRENT_RASTER_POSITION, pos); + if (texName) {//write the texture on screen + CGRect bounds = CGRectMake (pos[0], pos[1] - fl_descent(), w, h); + glPushAttrib(GL_ENABLE_BIT | GL_TEXTURE_BIT | GL_COLOR_BUFFER_BIT); // GL_COLOR_BUFFER_BIT for glBlendFunc, GL_ENABLE_BIT for glEnable / glDisable + + glDisable (GL_DEPTH_TEST); // ensure text is not removed by depth buffer test. + glEnable (GL_BLEND); // for text fading + glBlendFunc (GL_ONE, GL_ONE_MINUS_SRC_ALPHA); // ditto + glEnable (GL_TEXTURE_RECTANGLE_EXT); + + glBindTexture (GL_TEXTURE_RECTANGLE_EXT, texName); + glBegin (GL_QUADS); + glTexCoord2f (0.0f, 0.0f); // draw lower left in world coordinates + glVertex2f (bounds.origin.x, bounds.origin.y); + + glTexCoord2f (0.0f, h); // draw upper left in world coordinates + glVertex2f (bounds.origin.x, bounds.origin.y + bounds.size.height); + + glTexCoord2f (w, h); // draw upper right in world coordinates + glVertex2f (bounds.origin.x + bounds.size.width, bounds.origin.y + bounds.size.height); + + glTexCoord2f (w, 0.0f); // draw lower right in world coordinates + glVertex2f (bounds.origin.x + bounds.size.width, bounds.origin.y); + glEnd (); + + glPopAttrib(); + glDeleteTextures(1, &texName); + } + // reset original matrices + glPopMatrix(); // GL_MODELVIEW + glMatrixMode (GL_PROJECTION); + glPopMatrix(); + glMatrixMode (matrixMode); +//set the raster position to end of string + pos[0] += w; + GLdouble modelmat[16]; + glGetDoublev (GL_MODELVIEW_MATRIX, modelmat); + GLdouble projmat[16]; + glGetDoublev (GL_PROJECTION_MATRIX, projmat); + GLdouble objX, objY, objZ; + GLint viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + gluUnProject(pos[0], pos[1], pos[2], modelmat, projmat, viewport, &objX, &objY, &objZ); + glRasterPos2d(objX, objY); +} +#endif + #endif // diff --git a/src/makedepend b/src/makedepend index 425f4cf12..5ab041c44 100644 --- a/src/makedepend +++ b/src/makedepend @@ -383,9 +383,9 @@ Fl_Widget.o: ../FL/Fl_Tooltip.H ../FL/fl_draw.H ../FL/Fl_Window.H flstring.h Fl_Widget.o: ../config.h Fl_Window.o: ../config.h ../FL/Fl.H ../FL/fl_utf8.h ../FL/Fl_Export.H Fl_Window.o: ../FL/fl_types.h ../FL/Xutf8.h ../FL/Enumerations.H -Fl_Window.o: ../FL/Fl_Export.H ../FL/fl_types.h ../FL/Fl_Window.H -Fl_Window.o: ../FL/Fl_Group.H ../FL/Fl_Widget.H flstring.h ../FL/fl_draw.H -Fl_Window.o: ../FL/Fl_Window.H +Fl_Window.o: ../FL/Fl_Export.H ../FL/fl_types.h ../FL/x.H ../FL/Fl_Window.H +Fl_Window.o: ../FL/Fl_Window.H ../FL/Fl_Group.H ../FL/Fl_Widget.H flstring.h +Fl_Window.o: ../FL/fl_draw.H Fl_Window_fullscreen.o: ../FL/Fl.H ../FL/fl_utf8.h ../FL/Fl_Export.H Fl_Window_fullscreen.o: ../FL/fl_types.h ../FL/Xutf8.h ../FL/Enumerations.H Fl_Window_fullscreen.o: ../FL/Fl_Export.H ../FL/fl_types.h ../FL/x.H @@ -423,7 +423,7 @@ Fl_arg.o: ../FL/Fl_Widget.H ../FL/filename.H ../FL/fl_draw.H flstring.h Fl_arg.o: ../config.h Fl_compose.o: ../FL/Fl.H ../FL/fl_utf8.h ../FL/Fl_Export.H ../FL/fl_types.h Fl_compose.o: ../FL/Xutf8.h ../FL/Enumerations.H ../FL/Fl_Export.H -Fl_compose.o: ../FL/fl_types.h +Fl_compose.o: ../FL/fl_types.h ../FL/x.H ../FL/Fl_Window.H Fl_display.o: ../FL/Fl.H ../FL/fl_utf8.h ../FL/Fl_Export.H ../FL/fl_types.h Fl_display.o: ../FL/Xutf8.h ../FL/Enumerations.H ../FL/Fl_Export.H Fl_display.o: ../FL/fl_types.h flstring.h ../config.h diff --git a/src/screen_xywh.cxx b/src/screen_xywh.cxx index 66f05778d..3ef2aa970 100644 --- a/src/screen_xywh.cxx +++ b/src/screen_xywh.cxx @@ -107,7 +107,11 @@ static void screen_init() { #elif defined(__APPLE__) XRectangle screens[16]; +extern int MACscreen_init(XRectangle screens[]); static void screen_init() { +#ifdef __APPLE_COCOA__ + num_screens = MACscreen_init(screens); +#else GDHandle gd; for (gd = GetDeviceList(), num_screens = 0; gd; gd = GetNextDevice(gd)) { @@ -120,6 +124,7 @@ static void screen_init() { num_screens ++; if (num_screens >= 16) break; } +#endif } #elif HAVE_XINERAMA # include -- cgit v1.2.3