Implement monitoring of file descriptors in wxMotif event loop.
[wxWidgets.git] / src / motif / utils.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/utils.cpp
3 // Purpose: Various utilities
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
22
23 #include "wx/utils.h"
24
25 #ifndef WX_PRECOMP
26 #include "wx/app.h"
27 #include "wx/dcmemory.h"
28 #include "wx/bitmap.h"
29 #endif
30
31 #include "wx/apptrait.h"
32 #include "wx/evtloop.h"
33 #include "wx/private/eventloopsourcesmanager.h"
34 #include "wx/motif/private/timer.h"
35
36 #include <string.h>
37
38 #if (defined(__SUNCC__) || defined(__CLCC__))
39 #include <sysent.h>
40 #endif
41
42 #ifdef __VMS__
43 #pragma message disable nosimpint
44 #endif
45
46 #include <Xm/Xm.h>
47 #include <Xm/Frame.h>
48
49 #include "wx/motif/private.h"
50
51 #include "X11/Xutil.h"
52
53 #ifdef __VMS__
54 #pragma message enable nosimpint
55 #endif
56
57
58 // ============================================================================
59 // implementation
60 // ============================================================================
61
62 // ----------------------------------------------------------------------------
63 // async event processing
64 // ----------------------------------------------------------------------------
65
66 // Consume all events until no more left
67 void wxFlushEvents(WXDisplay* wxdisplay)
68 {
69 Display *display = (Display*)wxdisplay;
70 wxEventLoop evtLoop;
71
72 XSync (display, False);
73
74 while (evtLoop.Pending())
75 {
76 XFlush (display);
77 evtLoop.Dispatch();
78 }
79 }
80
81 #if wxUSE_EVENTLOOP_SOURCE
82
83 extern "C"
84 {
85
86 static
87 void
88 wxMotifInputHandler(XtPointer data,
89 int* WXUNUSED(fd),
90 XtInputId* WXUNUSED(inputId))
91 {
92 wxEventLoopSourceHandler * const
93 handler = static_cast<wxEventLoopSourceHandler *>(data);
94
95 handler->OnReadWaiting();
96 }
97
98 }
99
100 // This class exists just to call XtRemoveInput() in its dtor, the real work of
101 // dispatching events on the file descriptor to the handler is done by
102 // wxMotifInputHandler callback above.
103 class wxMotifEventLoopSource : public wxEventLoopSource
104 {
105 public:
106 wxMotifEventLoopSource(XtInputId inputId,
107 wxEventLoopSourceHandler *handler,
108 int flags)
109 : wxEventLoopSource(handler, flags),
110 m_inputId(inputId)
111 {
112 }
113
114 virtual ~wxMotifEventLoopSource()
115 {
116 XtRemoveInput(m_inputId);
117 }
118
119 private:
120 const XtInputId m_inputId;
121
122 wxDECLARE_NO_COPY_CLASS(wxMotifEventLoopSource);
123 };
124
125 class wxMotifEventLoopSourcesManager : public wxEventLoopSourcesManagerBase
126 {
127 public:
128 wxEventLoopSource *
129 AddSourceForFD(int fd, wxEventLoopSourceHandler* handler, int flags)
130 {
131 wxCHECK_MSG( wxTheApp, NULL, "Must create wxTheApp first" );
132
133 // The XtInputXXXMask values cannot be combined (hence "Mask" is a
134 // complete misnomer), and supporting those would make the code more
135 // complicated and we don't need them for now.
136 wxCHECK_MSG( !(flags & (wxEVENT_SOURCE_OUTPUT |
137 wxEVENT_SOURCE_EXCEPTION)),
138 NULL,
139 "Monitoring FDs for output/errors not supported" );
140
141 wxCHECK_MSG( flags & wxEVENT_SOURCE_INPUT,
142 NULL,
143 "Should be monitoring for input" );
144
145 XtInputId inputId = XtAppAddInput
146 (
147 (XtAppContext) wxTheApp->GetAppContext(),
148 fd,
149 (XtPointer) XtInputReadMask,
150 wxMotifInputHandler,
151 handler
152 );
153 if ( inputId < 0 )
154 return 0;
155
156 return new wxMotifEventLoopSource(inputId, handler, flags);
157 }
158 };
159
160 wxEventLoopSourcesManagerBase* wxGUIAppTraits::GetEventLoopSourcesManager()
161 {
162 static wxMotifEventLoopSourcesManager s_eventLoopSourcesManager;
163
164 return &s_eventLoopSourcesManager;
165 }
166
167 #endif // wxUSE_EVENTLOOP_SOURCE
168
169 // ----------------------------------------------------------------------------
170 // misc
171 // ----------------------------------------------------------------------------
172
173 // Emit a beeeeeep
174 void wxBell()
175 {
176 // Use current setting for the bell
177 XBell (wxGlobalDisplay(), 0);
178 }
179
180 wxPortId wxGUIAppTraits::GetToolkitVersion(int *verMaj, int *verMin) const
181 {
182 // XmVERSION and XmREVISION are defined in Xm/Xm.h
183 if ( verMaj )
184 *verMaj = XmVERSION;
185 if ( verMin )
186 *verMin = XmREVISION;
187
188 return wxPORT_MOTIF;
189 }
190
191 wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
192 {
193 return new wxEventLoop;
194 }
195
196 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer* timer)
197 {
198 return new wxMotifTimerImpl(timer);
199 }
200
201 // ----------------------------------------------------------------------------
202 // display info
203 // ----------------------------------------------------------------------------
204
205 void wxGetMousePosition( int* x, int* y )
206 {
207 #if wxUSE_NANOX
208 // TODO
209 *x = 0;
210 *y = 0;
211 #else
212 XMotionEvent xev;
213 Window root, child;
214 XQueryPointer(wxGlobalDisplay(),
215 DefaultRootWindow(wxGlobalDisplay()),
216 &root, &child,
217 &(xev.x_root), &(xev.y_root),
218 &(xev.x), &(xev.y),
219 &(xev.state));
220 *x = xev.x_root;
221 *y = xev.y_root;
222 #endif
223 }
224
225 // Return true if we have a colour display
226 bool wxColourDisplay()
227 {
228 return wxDisplayDepth() > 1;
229 }
230
231 // Returns depth of screen
232 int wxDisplayDepth()
233 {
234 Display *dpy = wxGlobalDisplay();
235
236 return DefaultDepth (dpy, DefaultScreen (dpy));
237 }
238
239 // Get size of display
240 void wxDisplaySize(int *width, int *height)
241 {
242 Display *dpy = wxGlobalDisplay();
243
244 if ( width )
245 *width = DisplayWidth (dpy, DefaultScreen (dpy));
246 if ( height )
247 *height = DisplayHeight (dpy, DefaultScreen (dpy));
248 }
249
250 void wxDisplaySizeMM(int *width, int *height)
251 {
252 Display *dpy = wxGlobalDisplay();
253
254 if ( width )
255 *width = DisplayWidthMM(dpy, DefaultScreen (dpy));
256 if ( height )
257 *height = DisplayHeightMM(dpy, DefaultScreen (dpy));
258 }
259
260 // Configurable display in wxX11 and wxMotif
261 static WXDisplay *gs_currentDisplay = NULL;
262 static wxString gs_displayName;
263
264 WXDisplay *wxGetDisplay()
265 {
266 if (gs_currentDisplay)
267 return gs_currentDisplay;
268 else if (wxTheApp)
269 return wxTheApp->GetInitialDisplay();
270 return NULL;
271 }
272
273 bool wxSetDisplay(const wxString& display_name)
274 {
275 gs_displayName = display_name;
276
277 if ( display_name.empty() )
278 {
279 gs_currentDisplay = NULL;
280
281 return true;
282 }
283 else
284 {
285 Cardinal argc = 0;
286
287 Display *display = XtOpenDisplay((XtAppContext) wxTheApp->GetAppContext(),
288 display_name.c_str(),
289 wxTheApp->GetAppName().c_str(),
290 wxTheApp->GetClassName().c_str(),
291 NULL,
292 #if XtSpecificationRelease < 5
293 0, &argc,
294 #else
295 0, (int *)&argc,
296 #endif
297 NULL);
298
299 if (display)
300 {
301 gs_currentDisplay = (WXDisplay*) display;
302 return true;
303 }
304 else
305 return false;
306 }
307 }
308
309 wxString wxGetDisplayName()
310 {
311 return gs_displayName;
312 }
313
314 wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
315 {
316 return wxGenericFindWindowAtPoint(pt);
317 }
318
319 // ----------------------------------------------------------------------------
320 // Some colour manipulation routines
321 // ----------------------------------------------------------------------------
322
323 void wxHSVToXColor(wxHSV *hsv,XColor *rgb)
324 {
325 int h = hsv->h;
326 int s = hsv->s;
327 int v = hsv->v;
328 int r = 0, g = 0, b = 0;
329 int i, f;
330 int p, q, t;
331 s = (s * wxMAX_RGB) / wxMAX_SV;
332 v = (v * wxMAX_RGB) / wxMAX_SV;
333 if (h == 360) h = 0;
334 if (s == 0) { h = 0; r = g = b = v; }
335 i = h / 60;
336 f = h % 60;
337 p = v * (wxMAX_RGB - s) / wxMAX_RGB;
338 q = v * (wxMAX_RGB - s * f / 60) / wxMAX_RGB;
339 t = v * (wxMAX_RGB - s * (60 - f) / 60) / wxMAX_RGB;
340 switch (i)
341 {
342 case 0: r = v, g = t, b = p; break;
343 case 1: r = q, g = v, b = p; break;
344 case 2: r = p, g = v, b = t; break;
345 case 3: r = p, g = q, b = v; break;
346 case 4: r = t, g = p, b = v; break;
347 case 5: r = v, g = p, b = q; break;
348 }
349 rgb->red = (unsigned short)(r << 8);
350 rgb->green = (unsigned short)(g << 8);
351 rgb->blue = (unsigned short)(b << 8);
352 }
353
354 void wxXColorToHSV(wxHSV *hsv,XColor *rgb)
355 {
356 int r = rgb->red >> 8;
357 int g = rgb->green >> 8;
358 int b = rgb->blue >> 8;
359 int maxv = wxMax3(r, g, b);
360 int minv = wxMin3(r, g, b);
361 int h = 0, s, v;
362 v = maxv;
363 if (maxv) s = (maxv - minv) * wxMAX_RGB / maxv;
364 else s = 0;
365 if (s == 0) h = 0;
366 else
367 {
368 int rc, gc, bc, hex = 0;
369 rc = (maxv - r) * wxMAX_RGB / (maxv - minv);
370 gc = (maxv - g) * wxMAX_RGB / (maxv - minv);
371 bc = (maxv - b) * wxMAX_RGB / (maxv - minv);
372 if (r == maxv) { h = bc - gc, hex = 0; }
373 else if (g == maxv) { h = rc - bc, hex = 2; }
374 else if (b == maxv) { h = gc - rc, hex = 4; }
375 h = hex * 60 + (h * 60 / wxMAX_RGB);
376 if (h < 0) h += 360;
377 }
378 hsv->h = h;
379 hsv->s = (s * wxMAX_SV) / wxMAX_RGB;
380 hsv->v = (v * wxMAX_SV) / wxMAX_RGB;
381 }
382
383 void wxAllocNearestColor(Display *d,Colormap cmp,XColor *xc)
384 {
385 #if !wxUSE_NANOX
386 int llp;
387
388 int screen = DefaultScreen(d);
389 int num_colors = DisplayCells(d,screen);
390
391 XColor *color_defs = new XColor[num_colors];
392 for(llp = 0;llp < num_colors;llp++) color_defs[llp].pixel = llp;
393 XQueryColors(d,cmp,color_defs,num_colors);
394
395 wxHSV hsv_defs, hsv;
396 wxXColorToHSV(&hsv,xc);
397
398 int diff, min_diff = 0, pixel = 0;
399
400 for(llp = 0;llp < num_colors;llp++)
401 {
402 wxXColorToHSV(&hsv_defs,&color_defs[llp]);
403 diff = wxSIGN(wxH_WEIGHT * (hsv.h - hsv_defs.h)) +
404 wxSIGN(wxS_WEIGHT * (hsv.s - hsv_defs.s)) +
405 wxSIGN(wxV_WEIGHT * (hsv.v - hsv_defs.v));
406 if (llp == 0) min_diff = diff;
407 if (min_diff > diff) { min_diff = diff; pixel = llp; }
408 if (min_diff == 0) break;
409 }
410
411 xc -> red = color_defs[pixel].red;
412 xc -> green = color_defs[pixel].green;
413 xc -> blue = color_defs[pixel].blue;
414 xc -> flags = DoRed | DoGreen | DoBlue;
415
416 /* FIXME, TODO
417 if (!XAllocColor(d,cmp,xc))
418 cout << "wxAllocNearestColor : Warning : Cannot find nearest color !\n";
419 */
420
421 delete[] color_defs;
422 #endif
423 }
424
425 void wxAllocColor(Display *d,Colormap cmp,XColor *xc)
426 {
427 if (!XAllocColor(d,cmp,xc))
428 {
429 // cout << "wxAllocColor : Warning : cannot allocate color, attempt find nearest !\n";
430 wxAllocNearestColor(d,cmp,xc);
431 }
432 }
433
434 wxString wxGetXEventName(XEvent& event)
435 {
436 #if wxUSE_NANOX
437 wxString str(wxT("(some event)"));
438 return str;
439 #else
440 int type = event.xany.type;
441 static char* event_name[] = {
442 wxMOTIF_STR(""), wxMOTIF_STR("unknown(-)"), // 0-1
443 wxMOTIF_STR("KeyPress"), wxMOTIF_STR("KeyRelease"), wxMOTIF_STR("ButtonPress"), wxMOTIF_STR("ButtonRelease"), // 2-5
444 wxMOTIF_STR("MotionNotify"), wxMOTIF_STR("EnterNotify"), wxMOTIF_STR("LeaveNotify"), wxMOTIF_STR("FocusIn"), // 6-9
445 wxMOTIF_STR("FocusOut"), wxMOTIF_STR("KeymapNotify"), wxMOTIF_STR("Expose"), wxMOTIF_STR("GraphicsExpose"), // 10-13
446 wxMOTIF_STR("NoExpose"), wxMOTIF_STR("VisibilityNotify"), wxMOTIF_STR("CreateNotify"), // 14-16
447 wxMOTIF_STR("DestroyNotify"), wxMOTIF_STR("UnmapNotify"), wxMOTIF_STR("MapNotify"), wxMOTIF_STR("MapRequest"),// 17-20
448 wxMOTIF_STR("ReparentNotify"), wxMOTIF_STR("ConfigureNotify"), wxMOTIF_STR("ConfigureRequest"), // 21-23
449 wxMOTIF_STR("GravityNotify"), wxMOTIF_STR("ResizeRequest"), wxMOTIF_STR("CirculateNotify"), // 24-26
450 wxMOTIF_STR("CirculateRequest"), wxMOTIF_STR("PropertyNotify"), wxMOTIF_STR("SelectionClear"), // 27-29
451 wxMOTIF_STR("SelectionRequest"), wxMOTIF_STR("SelectionNotify"), wxMOTIF_STR("ColormapNotify"), // 30-32
452 wxMOTIF_STR("ClientMessage"), wxMOTIF_STR("MappingNotify"), // 33-34
453 wxMOTIF_STR("unknown(+)")}; // 35
454 type = wxMin(35, type); type = wxMax(1, type);
455 wxString str(event_name[type]);
456 return str;
457 #endif
458 }
459
460 // ----------------------------------------------------------------------------
461 // accelerators
462 // ----------------------------------------------------------------------------
463
464 // Find the letter corresponding to the mnemonic, for Motif
465 char wxFindMnemonic (const char *s)
466 {
467 char mnem = 0;
468 int len = strlen (s);
469 int i;
470
471 for (i = 0; i < len; i++)
472 {
473 if (s[i] == '&')
474 {
475 // Carefully handle &&
476 if ((i + 1) <= len && s[i + 1] == '&')
477 i++;
478 else
479 {
480 mnem = s[i + 1];
481 break;
482 }
483 }
484 }
485 return mnem;
486 }
487
488 char* wxFindAccelerator( const char *s )
489 {
490 #if 1
491 wxUnusedVar(s);
492 // VZ: this function returns incorrect keysym which completely breaks kbd
493 // handling
494 return NULL;
495 #else
496 // The accelerator text is after the \t char.
497 s = strchr( s, '\t' );
498
499 if( !s ) return NULL;
500
501 /*
502 Now we need to format it as X standard:
503
504 input output
505
506 F7 --> <Key>F7
507 Ctrl+N --> Ctrl<Key>N
508 Alt+k --> Meta<Key>k
509 Ctrl+Shift+A --> Ctrl Shift<Key>A
510
511 and handle Ctrl-N & similia
512 */
513
514 static char buf[256];
515
516 buf[0] = '\0';
517 wxString tmp = s + 1; // skip TAB
518 size_t index = 0;
519
520 while( index < tmp.length() )
521 {
522 size_t plus = tmp.find( '+', index );
523 size_t minus = tmp.find( '-', index );
524
525 // neither '+' nor '-', add <Key>
526 if( plus == wxString::npos && minus == wxString::npos )
527 {
528 strcat( buf, "<Key>" );
529 strcat( buf, tmp.c_str() + index );
530
531 return buf;
532 }
533
534 // OK: npos is big and positive
535 size_t sep = wxMin( plus, minus );
536 wxString mod = tmp.substr( index, sep - index );
537
538 // Ctrl -> Ctrl
539 // Shift -> Shift
540 // Alt -> Meta
541 if( mod == "Alt" )
542 mod = "Meta";
543
544 if( buf[0] )
545 strcat( buf, " " );
546
547 strcat( buf, mod.c_str() );
548
549 index = sep + 1;
550 }
551
552 return NULL;
553 #endif
554 }
555
556 XmString wxFindAcceleratorText (const char *s)
557 {
558 #if 1
559 wxUnusedVar(s);
560 // VZ: this function returns incorrect keysym which completely breaks kbd
561 // handling
562 return NULL;
563 #else
564 // The accelerator text is after the \t char.
565 s = strchr( s, '\t' );
566
567 if( !s ) return NULL;
568
569 return wxStringToXmString( s + 1 ); // skip TAB!
570 #endif
571 }
572
573 // Change a widget's foreground and background colours.
574 void wxDoChangeForegroundColour(WXWidget widget, wxColour& foregroundColour)
575 {
576 if (!foregroundColour.IsOk())
577 return;
578
579 // When should we specify the foreground, if it's calculated
580 // by wxComputeColours?
581 // Solution: say we start with the default (computed) foreground colour.
582 // If we call SetForegroundColour explicitly for a control or window,
583 // then the foreground is changed.
584 // Therefore SetBackgroundColour computes the foreground colour, and
585 // SetForegroundColour changes the foreground colour. The ordering is
586 // important.
587
588 XtVaSetValues ((Widget) widget,
589 XmNforeground, foregroundColour.AllocColour(XtDisplay((Widget) widget)),
590 NULL);
591 }
592
593 void wxDoChangeBackgroundColour(WXWidget widget, const wxColour& backgroundColour, bool changeArmColour)
594 {
595 if (!backgroundColour.IsOk())
596 return;
597
598 wxComputeColours (XtDisplay((Widget) widget), & backgroundColour,
599 NULL);
600
601 XtVaSetValues ((Widget) widget,
602 XmNbackground, g_itemColors[wxBACK_INDEX].pixel,
603 XmNtopShadowColor, g_itemColors[wxTOPS_INDEX].pixel,
604 XmNbottomShadowColor, g_itemColors[wxBOTS_INDEX].pixel,
605 XmNforeground, g_itemColors[wxFORE_INDEX].pixel,
606 NULL);
607
608 if (changeArmColour)
609 XtVaSetValues ((Widget) widget,
610 XmNarmColor, g_itemColors[wxSELE_INDEX].pixel,
611 NULL);
612 }
613
614 extern void wxDoChangeFont(WXWidget widget, const wxFont& font)
615 {
616 // Lesstif 0.87 hangs here, but 0.93 does not; MBN: sometimes it does
617 #if !wxCHECK_LESSTIF() // || wxCHECK_LESSTIF_VERSION( 0, 93 )
618 Widget w = (Widget)widget;
619 XtVaSetValues( w,
620 wxFont::GetFontTag(), font.GetFontTypeC( XtDisplay(w) ),
621 NULL );
622 #else
623 wxUnusedVar(widget);
624 wxUnusedVar(font);
625 #endif
626
627 }
628
629 wxString wxXmStringToString( const XmString& xmString )
630 {
631 char *txt;
632 if( XmStringGetLtoR( xmString, XmSTRING_DEFAULT_CHARSET, &txt ) )
633 {
634 wxString str(txt);
635 XtFree (txt);
636 return str;
637 }
638
639 return wxEmptyString;
640 }
641
642 XmString wxStringToXmString( const char* str )
643 {
644 return XmStringCreateLtoR((char *)str, XmSTRING_DEFAULT_CHARSET);
645 }
646
647 // ----------------------------------------------------------------------------
648 // wxBitmap utility functions
649 // ----------------------------------------------------------------------------
650
651 // Creates a bitmap with transparent areas drawn in
652 // the given colour.
653 wxBitmap wxCreateMaskedBitmap(const wxBitmap& bitmap, const wxColour& colour)
654 {
655 wxBitmap newBitmap(bitmap.GetWidth(),
656 bitmap.GetHeight(),
657 bitmap.GetDepth());
658 wxMemoryDC destDC;
659 wxMemoryDC srcDC;
660
661 srcDC.SelectObjectAsSource(bitmap);
662 destDC.SelectObject(newBitmap);
663
664 wxBrush brush(colour, wxSOLID);
665 destDC.SetBackground(brush);
666 destDC.Clear();
667 destDC.Blit(0, 0, bitmap.GetWidth(), bitmap.GetHeight(),
668 &srcDC, 0, 0, wxCOPY, true);
669
670 return newBitmap;
671 }
672
673 // ----------------------------------------------------------------------------
674 // Miscellaneous functions
675 // ----------------------------------------------------------------------------
676
677 WXWidget wxCreateBorderWidget( WXWidget parent, long style )
678 {
679 Widget borderWidget = (Widget)NULL, parentWidget = (Widget)parent;
680
681 if (style & wxSIMPLE_BORDER)
682 {
683 borderWidget = XtVaCreateManagedWidget
684 (
685 "simpleBorder",
686 xmFrameWidgetClass, parentWidget,
687 XmNshadowType, XmSHADOW_ETCHED_IN,
688 XmNshadowThickness, 1,
689 NULL
690 );
691 }
692 else if ((style & wxSUNKEN_BORDER) || (style & wxBORDER_THEME))
693 {
694 borderWidget = XtVaCreateManagedWidget
695 (
696 "sunkenBorder",
697 xmFrameWidgetClass, parentWidget,
698 XmNshadowType, XmSHADOW_IN,
699 NULL
700 );
701 }
702 else if (style & wxRAISED_BORDER)
703 {
704 borderWidget = XtVaCreateManagedWidget
705 (
706 "raisedBorder",
707 xmFrameWidgetClass, parentWidget,
708 XmNshadowType, XmSHADOW_OUT,
709 NULL
710 );
711 }
712
713 return borderWidget;
714 }