1/* sqUnixQuartz.m -- display via native windows on Mac OS X	-*- ObjC -*-
2 *
3 * Author: Ian Piumarta <ian.piumarta@squeakland.org>
4 *
5 *   Copyright (C) 1996-2007 by Ian Piumarta and other authors/contributors
6 *                              listed elsewhere in this file.
7 *   All rights reserved.
8 *
9 *   Permission is hereby granted, free of charge, to any person obtaining a copy
10 *   of this software and associated documentation files (the "Software"), to deal
11 *   in the Software without restriction, including without limitation the rights
12 *   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 *   copies of the Software, and to permit persons to whom the Software is
14 *   furnished to do so, subject to the following conditions:
15 *
16 *   The above copyright notice and this permission notice shall be included in
17 *   all copies or substantial portions of the Software.
18 *
19 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 *   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 *   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 *   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 *   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 *   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25 *   SOFTWARE.
26 *
27 * Last edited: 2009-08-19 04:38:45 by piumarta on emilia-2.local
28 */
29
30
31//xxx ...
32//
33// check use of winRect and titleRect.  reduce to (int)winHeight and
34// (int)titleHeight in setRects.
35//
36// investigate creating sq events in the UI thread and then sending
37// them down the notification pipe, instead of locking the event
38// queue.
39
40
41#import <Cocoa/Cocoa.h>
42
43#include "sqUnixMain.h"
44#include "sqUnixGlobals.h"
45#include "sqUnixCharConv.h"
46
47#include "sq.h"
48#include "sqaio.h"
49
50#include <unistd.h>
51#include <stdlib.h>
52#include <stdarg.h>
53#include <stdio.h>
54#include <string.h>
55#include <sys/param.h>
56#include <sys/types.h>
57#include <sys/stat.h>
58#include <sys/socket.h>
59#include <sys/ioctl.h>
60#include <fcntl.h>
61#include <pthread.h>
62#include <sched.h>
63
64#include "debug.h"
65
66#include "config.h"
67#undef HAVE_GL_GL_H
68#include "SqDisplay.h"
69
70
71///
72/// Things you can tweak, if you're curious/bored enough to want to...
73///
74
75// do we draw the current screen extent (width x height) in the title
76// bar during live resize?
77//
78#define	RESIZE_IN_TITLE	 1
79
80// how large a (square) area, in the lower right of the window, should
81// respond to mouse down by initiating window resize?  (the resize
82// icon itself is, when we allow it to be shown, 13x13 pixels.)
83//
84#define	RESIZE_EXTENT	 8
85
86// do we fade the screen out and back in gently when changing to
87// fullscreen mode, or just switch with one big violent click?  if
88// this is defined then it represents the incr/decrement per
89// millisecond of the gamma multiplier (1.0 to 0.0 and back); if
90// undefined then the switch is immediate.
91//
92#undef	FULLSCREEN_FADE	 0.02
93
94///
95/// No more user-serviceable parts in this file.  Stop Tweaking Now!
96///
97
98#if defined (__ppc__)
99# define USE_SPINLOCK	1
100#else
101# define USE_SPINLOCK	0
102#endif
103#define USE_OWN_ICON	0
104
105static inline int min(int x, int y) { return x < y ? x : y; }
106static inline int max(int x, int y) { return x > y ? x : y; }
107
108
109// -*- ObjC -*-
110
111@interface Squeak : NSApplication
112- (void) applicationDidFinishLaunching: (NSNotification *)note;
113- (void) applicationDidChangeScreenParameters: (NSNotification *)note;
114- (void) unhideAllApplications: (id)sender;
115- (BOOL) windowShouldClose: (id)sender;
116- (void) maybeTerminate: (id)sender;
117- (void) terminate: (id)sender;
118- (void) performEnableKeys: (id)sender;
119- (void) performDisableKeys: (id)sender;
120@end
121
122
123@interface SqueakWindow : NSWindow
124{
125  NSImage *icon;
126}
127- (BOOL) isOpaque;
128- (BOOL) canBecomeKeyWindow;
129- (void) setIcon;
130#if 0
131- (NSImage *) dockImage
132- (void) miniaturize: (id)sender;
133#endif
134- (void) performMiniaturize: (id)sender;
135@end
136
137
138// Why QDView?  Well...
139//
140//   1) we can trivially obtain a raw pointer to its backing store, so
141//   2) no need to putz around with the lockFocus/DataProvider/ImageRep crap; plus
142//   3) its buffer's coordinate system is already the right way up for Squeak, so
143//   4) we avoid potential recopy (just to have CG recopy again); besides
144//   5) QDFlushBuffer is _blindingly_ fast (even compared to drawing directly on
145//      the framebuffer [go measure it if you don't believe me]); but most importantly
146//   6) the QD API is completely free of ObjC and attendant inefficiencies.
147
148@interface SqueakView : NSQuickDrawView <NSTextInput>
149- (BOOL) acceptsFirstResponder;
150- (BOOL) becomeFirstResponder;
151- (BOOL) isOpaque;
152- (BOOL) isFlipped;
153- (id)   initWithFrame: (NSRect)frame;
154- (void) setFrame: (NSRect)rect;
155- (void) drawRect: (NSRect)rect;
156- (void) viewWillStartLiveResize;
157- (void) viewDidEndLiveResize;
158- (int)  draggingEntered: (id<NSDraggingInfo>)sender;
159- (int)  draggingUpdated: (id<NSDraggingInfo>)sender;
160- (void) draggingExited: (id<NSDraggingInfo>)sender;
161- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender;
162- (int) composeKeyDown: (NSEvent *)event;
163- (int) composeKeyUp: (NSEvent *)event;
164@end
165
166
167static SqueakView	*view	    = 0; /* app view (occupies topRect)		 */
168
169
170@interface TopView : NSView
171- (void) setFrame: (NSRect)rect;
172@end
173
174@implementation TopView
175- (void) setFrame: (NSRect)rect
176{
177  [super setFrame: rect];
178  if (view) [view setFrame: rect];
179}
180@end
181
182
183static int		 styleMask  = 0; /* window style mask			*/
184static int		 dragCount  = 0; /* number of items during drag/drop	*/
185static int		 showExtent = 0; /* 1 if title bar shows view extent	*/
186       int		 inModalLoop= 0; /* 1 when WS is in tracking loop	*/
187static int		 active     = 0; /* 1 when app window is active		*/
188
189//static CFArrayRef	   dpyModes   = 0; /* one of these days... */
190
191static CGDirectDisplayID dpy	    = 0;
192static NSDictionary	*dpyMode    = 0;
193static int		 dpyWidth   = 0;
194static int		 dpyHeight  = 0;
195static int		 dpyDepth   = 0;
196       char		*dpyPixels  = 0;
197       int		 dpyPitch   = 0;
198
199static SqueakWindow	*win	    = 0; /* main application window		 */
200static NSRect		 topRect;	 /* main window frame (excl. decoration) */
201static NSRect		 titleRect;	 /* decoration area (above topRect)	 */
202static NSRect		 resizeRect;	 /* area sensitive to resize		 */
203
204static TopView		*topView    = 0; /* top view (occupies topRect)		 */
205
206static char		*pixBase    = 0;
207static int		 pixWidth   = 0; /* pixmap width (pixels)		 */
208static int		 pixHeight  = 0; /* pixmap height			 */
209static int		 pixDepth   = 0; /* pixmap depth			 */
210static int		 pixPitch   = 0;
211static RgnHandle	 pixRegion  = 0;
212
213static int		 cmdKeys    = 0; /* 1 if app command keys enabled	 */
214static int		 fromFinder = 0; /* 1 if app launched from finder	 */
215static int		 noTitle    = 0; /* 1 if app window is undecorated	 */
216static int		 headless   = 0; /* 1 if app has no window		 */
217static int		 noDock     = 0; /* 1 if app window is undocked		 */
218static int		 fullscreen = 0; /* 1 if window fullscreen and on top	 */
219
220static char		*clipboard  = 0;
221
222static int		 stXfd	    = -1;
223static int		 osXfd	    = -1;
224
225#if (USE_SPINLOCK)
226static int		 displayMx  = 0;
227#else
228static pthread_mutex_t	 displayMx  = PTHREAD_MUTEX_INITIALIZER;
229#endif
230
231static char		 resourcePath[MAXPATHLEN];
232
233static int  glActive= 0;
234static void reframeRenderers(void);
235static void updateRenderers(void);
236
237
238
239#if 0 //xxx REMOVE ME
240
241#define RED	0xff0000
242#define GREEN	0x00ff00
243#define BLUE	0x0000ff
244#define WHITE	0xffffff
245#define BLACK	0x000000
246
247void feedback(int offset, int pixel)
248{
249  const int width= 4, height= 4;
250  long *base=  CGDisplayBaseAddress(dpy);
251  int   pitch= CGDisplayBytesPerRow(dpy);
252  int x, y;
253
254  base= base + width * offset;
255
256  for (y= 0; y < height; ++y)
257    {
258      for (x= 0; x < width; ++x)
259	base[x]= pixel;
260      base= (long *)((char *)base + pitch);
261    }
262}
263
264#endif
265
266
267#if (USE_SPINLOCK)
268
269extern inline int testAndSet(int *lock)
270{
271  int valu;
272# if defined(__i386__)
273  asm volatile("        movl	$1, %0		\n"
274               "        xchg	%0, %1		\n"
275               : "=&r"(valu) : "r"(lock));
276# else
277  asm volatile("        lwarx   %0, 0, %1       \n"
278               "        cmpwi   %0, 0           \n"
279               "        bne-    1f              \n"
280               "        li      %0, 1           \n"
281               "        stwcx.  %0, 0, %1       \n"
282               "        bne-    1f              \n"
283               "        li      %0, 0           \n"
284               "1:                              \n"
285               : "=&r"(valu) : "r"(lock) : "cr0","memory");
286# endif
287  return valu;
288}
289
290extern inline int doLock(int *lock, const char *who)
291{
292  while (testAndSet(lock))
293    ;
294  return 1;
295}
296
297extern inline void doUnlock(int *lock)
298{
299  *lock= 0;
300}
301
302#else
303
304//xxx FIXME SOON: check all uses of lock() and conditionalise the
305//  sections where failure could cause SEGV (rather than just
306//  inconsistent geometry or whatever)
307
308static int doLock(pthread_mutex_t *mx, char *who)
309{
310  static char *owner= "<none>";
311  int backoff, i= 0;
312  // wait about 1 second before giving up (10000 == timeslice quantum)
313  for (backoff= 10000;  backoff < 1280000;  ++i, backoff <<= 1)
314    if (0 == pthread_mutex_trylock(mx))
315      {
316	owner= who;
317	return 1;
318      }
319    else
320      {
321#      ifndef NDEBUG
322	fprintf(stderr, "lock %d: %s waiting for %-20s\n", i, who, owner);
323#      endif
324	usleep(backoff);
325      }
326  perror("pthread_mutex_trylock");
327  return 0;
328}
329
330static void doUnlock(pthread_mutex_t *mx)
331{
332  if (pthread_mutex_unlock(mx))
333    perror("pthread_mutex_unlock");
334}
335
336#endif
337
338
339#define lock(MX)	doLock(&MX##Mx, __FUNCTION__)
340#define unlock(MX)	doUnlock(&MX##Mx)
341
342
343#include "sqUnixEvent.c"
344
345
346
347//xxx FIXME: this is currently monochrome
348
349static sqInt display_ioFormPrint(sqInt bitsAddr, sqInt width, sqInt height, sqInt depth,
350				 double hScale, double vScale, sqInt landscapeFlag)
351{
352  //xxx hScale and vScale are ppi.  is there a way to use this
353  // meaningfully with PrintInfo or NSPrinter?
354
355  NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
356  int opp=     depth / 8;
357  int success= 1;
358
359  debugf(("ioFormPrint %f %f\n", hScale, vScale));
360  {
361    unsigned char    *planes[1]= { (unsigned char *)pointerForOop(bitsAddr) };
362    NSBitmapImageRep *bitmap= 	 0;
363    NSImage	     *image=  	 0;
364    NSImageView	     *view=   	 0;
365
366    bitmap= [[NSBitmapImageRep alloc]
367	      initWithBitmapDataPlanes: planes
368	      pixelsWide:		width
369	      pixelsHigh:		height
370	      bitsPerSample:		depth
371	      samplesPerPixel:		1
372	      hasAlpha:			NO
373	      isPlanar:			NO
374	      colorSpaceName:		NSCalibratedBlackColorSpace
375	      bytesPerRow:		width * opp
376	      bitsPerPixel:		depth];
377    if (!bitmap) { debugf(("bitmap fail\n")); success= 0; goto done; }
378    image= [NSImage alloc];
379    //[image setSize: NSMakeSize(width, height)];
380    [image addRepresentation: bitmap];
381    if (!image) { debugf(("image fail\n")); success= 0; goto done; }
382    view= [[NSImageView alloc] initWithFrame: NSMakeRect(0, 0, width, height)];
383    [view setImage: image];
384    {
385      NSPrintOperation *op=  [NSPrintOperation printOperationWithView: view];
386      [op setShowPanels: YES];
387      debugf(("launch print operation\n"));
388      [op runOperation];
389    }
390  }
391
392 done:
393  debugf(("ioFormPrint done.\n"));
394  [pool release];
395  return success;
396}
397
398
399static sqInt display_ioBeep(void)
400{
401  NSBeep();
402  return 0;
403}
404
405
406static sqInt display_ioRelinquishProcessorForMicroseconds(sqInt microSeconds)
407{
408  aioSleep(microSeconds);
409  return 0;
410}
411
412
413
414///
415/// events
416///
417
418
419static unsigned int qz2sqModifiers(unsigned int qz)
420{
421  return
422    ( ((qz & (NSShiftKeyMask | NSAlphaShiftKeyMask)) ? ShiftKeyBit   : 0))
423    | ((qz &  NSControlKeyMask)			     ? CtrlKeyBit    : 0)
424    | ((qz &  NSAlternateKeyMask)		     ? OptionKeyBit  : 0)
425    | ((qz &  NSCommandKeyMask)			     ? CommandKeyBit : 0);
426}
427
428static unsigned int qz2sqButton(unsigned int button)
429{
430  // the image has blue and yellow back-to-front.  fix that here...
431  switch (button)
432    {
433    case 0: return RedButtonBit;
434    case 1: return (swapBtn ? YellowButtonBit : BlueButtonBit);
435    case 2: return (swapBtn ? BlueButtonBit   : YellowButtonBit);
436    }
437  debugf(("unknown mouse button %d\n", button));
438  return RedButtonBit;
439}
440
441
442static unsigned int qz2sqKey(NSEvent *event)
443{
444  NSAutoreleasePool *pool=  [[NSAutoreleasePool alloc] init];
445  NSString	    *chars= [event characters];
446  UInt32	     enc=   CFStringConvertEncodingToNSStringEncoding((CFStringEncoding)sqTextEncoding);
447  NSData	    *data=  [chars dataUsingEncoding: enc allowLossyConversion: NO];
448  int keyCode= -1;
449
450# define returnKey(N)	keyCode= (N);  goto done
451
452  if ([data length])
453    {
454      keyCode= ((unsigned char *)[data bytes])[0];
455      if (keyCode == 127)
456	keyCode= 8;
457      goto done;
458    }
459
460  if ([chars length])
461    {
462      keyCode= [chars characterAtIndex: 0];
463      switch (keyCode)
464	{
465	case NSDeleteFunctionKey:	returnKey( 8);
466	case NSUpArrowFunctionKey:	returnKey(30);
467	case NSDownArrowFunctionKey:	returnKey(31);
468	case NSLeftArrowFunctionKey:	returnKey(28);
469	case NSRightArrowFunctionKey:	returnKey(29);
470	case NSHomeFunctionKey:		returnKey( 1);
471	case NSEndFunctionKey:		returnKey( 4);
472	case NSPageUpFunctionKey:	returnKey(11);
473	case NSPageDownFunctionKey:	returnKey(12);
474	  /* -- these should probably be implemented -- */
475#	define unknown(KEY) printf(KEY"\n"); returnKey(-1)
476	case NSClearLineFunctionKey:	unknown("Clear/Num Lock");
477	case NSHelpFunctionKey:		unknown("Help");
478	  /* -- the rest are missing on most mac keyboards -- */
479	case NSBeginFunctionKey:	unknown("Begin");
480	case NSPrintScreenFunctionKey:	unknown("Print Screen");
481	case NSScrollLockFunctionKey:	unknown("Scroll Lock");
482	case NSPauseFunctionKey:	unknown("Pause");
483	case NSSysReqFunctionKey:	unknown("System Request");
484	case NSBreakFunctionKey:	unknown("Break");
485	case NSResetFunctionKey:	unknown("Reset");
486	case NSStopFunctionKey:		unknown("Stop");
487	case NSMenuFunctionKey:		unknown("Menu");
488	case NSUserFunctionKey:		unknown("User");
489	case NSSystemFunctionKey:	unknown("System");
490	case NSPrintFunctionKey:	unknown("Print");
491	case NSClearDisplayFunctionKey:	unknown("Clear Display");
492	case NSInsertLineFunctionKey:	unknown("Insert Line");
493	case NSDeleteLineFunctionKey:	unknown("Delete Line");
494	case NSInsertCharFunctionKey:	unknown("Insert Character");
495	case NSDeleteCharFunctionKey:	unknown("Delete Character");
496	case NSPrevFunctionKey:		unknown("Previous");
497	case NSNextFunctionKey:		unknown("Next");
498	case NSSelectFunctionKey:	unknown("Select");
499	case NSExecuteFunctionKey:	unknown("Execute");
500	case NSUndoFunctionKey:		unknown("Undo");
501	case NSRedoFunctionKey:		unknown("Redo");
502	case NSFindFunctionKey:		unknown("Find");
503	case NSModeSwitchFunctionKey:	unknown("Mode Switch");
504#	undef unknown
505	}
506      if (keyCode & 0xff00)
507	keyCode= -1;
508    }
509
510 done:
511  [pool release];
512  return keyCode;
513}
514
515
516
517static inline void noteMousePoint(NSPoint loc)
518{
519  int x= (int)loc.x;
520  int y= (int)topRect.size.height - (int)loc.y;
521  // mouse motion/up is tracked outside of topRect when active, so
522  // clamp it explicitly
523  // (note: there's a race here, but it's benign)
524  mousePosition.x= max(0, min(x, pixWidth  - 1));
525  mousePosition.y= max(0, min(y, pixHeight - 1));
526}
527
528
529static void evtHandler(int fd, void *data, int flags)
530{
531  for (;;)
532    {
533      sqInputEvent evt;
534      int n= read(fd, (void *)&evt, sizeof(evt));
535      if (n < 0)
536	{
537	  if ((EINTR == errno) || (EAGAIN == errno))
538	    break;
539	  perror("evtHandler: read");
540	}
541      else if (n == 0)
542	break;
543      else if (n != sizeof(evt))
544	fprintf(stderr, "evtHandler: read returned %d -- why?\n", n);
545      else
546	{
547	  sqInputEvent *evp= allocateInputEvent(0);
548	  *evp= evt;
549	  signalInputEvent();
550	}
551    }
552  aioHandle(fd, evtHandler, AIO_RX);
553}
554
555
556static void sendEvent(sqInputEvent *evt)
557{
558  if (inModalLoop)    //xxx there are other ways to escape from one of these
559    inModalLoop= 0;
560  if (sizeof(*evt) != write(osXfd, evt, sizeof(*evt)))
561    perror("sendEvent: write");
562}
563
564
565static int makeButtonState(void)
566{
567  int btn= buttonState;
568  int mod= modifierState;
569  if (btn == RedButtonBit)
570    switch (mod)
571      {
572      case OptionKeyBit:	btn= YellowButtonBit;	mod= 0;	break;
573      case CommandKeyBit:	btn= BlueButtonBit;	mod= 0;	break;
574      }
575  return (mod << 3) | btn;
576}
577
578
579static void noteMouseEvent(void)
580{
581  int state= makeButtonState();
582  sqMouseEvent evt;
583  evt.type= EventTypeMouse;
584  evt.timeStamp= ioMSecs();
585  evt.x= mousePosition.x;
586  evt.y= mousePosition.y;
587  evt.buttons= (state & 0x7);
588  evt.modifiers= (state >> 3);
589  evt.reserved1= 0;
590  evt.windowIndex= 0;
591#ifdef DEBUG_EVENTS
592  printf("EVENT: mouse (%d,%d)", mousePosition.x, mousePosition.y);
593  printModifiers(state >> 3);
594  printButtons(state & 7);
595  printf("\n");
596#endif
597  sendEvent((sqInputEvent *)&evt);
598}
599
600
601static void noteKeyboardEvent(int keyCode, int pressCode, int modifiers)
602{
603  sqKeyboardEvent evt;
604  evt.type= EventTypeKeyboard;
605  evt.timeStamp= ioMSecs();
606  evt.charCode= keyCode;
607  evt.pressCode= pressCode;
608  evt.modifiers= modifiers;
609  evt.utf32Code= 0;	/* xxx TODO xxx */
610  evt.reserved1= 0;
611  evt.windowIndex= 0;
612#ifdef DEBUG_EVENTS
613  printf("EVENT: keyboard");
614  printModifiers(modifiers);
615  printKey(keyCode);
616  printf("\n");
617#endif
618  sendEvent((sqInputEvent *)&evt);
619}
620
621
622static void noteDragEvent(int dragType, int numFiles)
623{
624  int state= makeButtonState();
625  sqDragDropFilesEvent evt;
626  evt.type= EventTypeDragDropFiles;
627  evt.timeStamp= ioMSecs();
628  evt.dragType= dragType;
629  evt.x= mousePosition.x;
630  evt.y= mousePosition.y;
631  evt.modifiers= (state >> 3);
632  evt.numFiles= numFiles;
633  evt.windowIndex= 0;
634  sendEvent((sqInputEvent *)&evt);
635}
636
637
638static sqInt display_ioProcessEvents(void)
639{
640  return aioPoll(0);
641}
642
643
644static sqInt display_ioScreenDepth(void)
645{
646  return headless ? 1 : dpyDepth;
647}
648
649static int displayChanged= 0;
650
651static sqInt display_ioScreenSize(void)
652{
653  int size;
654  if (headless)
655    return ((16 << 16) | 16);
656  if (displayChanged)
657    {
658      displayChanged= 0;
659      [win setFrame: [win frame] display: YES];
660      return 0;
661    }
662  lock(display);
663  size= getSavedWindowSize();
664  unlock(display);
665  return size;
666}
667
668
669static sqInt display_ioSetCursorWithMask(sqInt cursorBitsIndex, sqInt cursorMaskIndex, sqInt offsetX, sqInt offsetY)
670{
671  if (headless)
672    return 0;
673
674  if ([view lockFocusIfCanDraw])
675    {
676      NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
677      NSBitmapImageRep *bitmap= 0;
678      NSImage          *image=  0;
679      NSCursor         *cursor= 0;
680
681      if (cursorMaskIndex == 0)
682	cursorMaskIndex= cursorBitsIndex;
683
684      bitmap= [[NSBitmapImageRep alloc]
685		initWithBitmapDataPlanes: 0 pixelsWide: 16 pixelsHigh: 16
686		bitsPerSample: 1 samplesPerPixel: 2
687		hasAlpha: YES isPlanar: YES
688		colorSpaceName: NSCalibratedBlackColorSpace
689		bytesPerRow: 2
690		bitsPerPixel: 0];
691      {
692	unsigned char *planes[5];
693	[bitmap getBitmapDataPlanes: planes];
694	{
695	  unsigned char *data= planes[0];
696	  unsigned char *mask= planes[1];
697	  int            i;
698
699	  for (i= 0; i < 16; ++i)
700	    {
701	      unsigned int word= ((unsigned int *)pointerForOop(cursorBitsIndex))[i];
702	      data[i*2 + 0]= (word >> 24) & 0xFF;
703	      data[i*2 + 1]= (word >> 16) & 0xFF;
704	      word= ((unsigned int *)pointerForOop(cursorMaskIndex))[i];
705	      mask[i*2 + 0]= (word >> 24) & 0xFF;
706	      mask[i*2 + 1]= (word >> 16) & 0xFF;
707	    }
708	}
709      }
710      image= [[NSImage alloc] init];
711      [image addRepresentation: bitmap];
712      {
713	NSPoint hotSpot= { -offsetX, -offsetY };
714	cursor= [[NSCursor alloc] initWithImage: image hotSpot: hotSpot];
715      }
716      [cursor set];
717      [pool release];
718      [view unlockFocus];
719    }
720  return 1;
721}
722
723static sqInt display_ioSetCursorARGB(sqInt cursorBitsIndex, sqInt extentX, sqInt extentY, sqInt offsetX, sqInt offsetY)
724{
725  if (headless)
726    return 0;
727
728  if ([view lockFocusIfCanDraw])
729    {
730      NSAutoreleasePool *pool= [[NSAutoreleasePool alloc] init];
731      NSBitmapImageRep  *bitmap= 0;
732      NSImage           *image=  0;
733      NSCursor          *cursor= 0;
734
735      bitmap= [[NSBitmapImageRep alloc]
736		initWithBitmapDataPlanes: 0 pixelsWide: extentX pixelsHigh: extentY
737		bitsPerSample: 8 samplesPerPixel: 4
738		hasAlpha: YES isPlanar: NO
739		colorSpaceName: NSCalibratedRGBColorSpace
740		bytesPerRow: extentX * 4
741		bitsPerPixel: 0];
742      {
743	unsigned *planes[5];
744	[bitmap getBitmapDataPlanes: planes];
745	unsigned* src= (unsigned*)cursorBitsIndex;
746	unsigned* dst= planes[0];
747	int i;
748	for (i= 0;  i < extentX * extentY;  ++i, ++dst, ++src)
749	  *dst= (*src & 0xFF00FF00) | ((*src & 0x000000FF) << 16) | ((*src & 0x00FF0000) >> 16);
750      }
751      image= [[NSImage alloc] init];
752      [image addRepresentation: bitmap];
753      {
754	NSPoint hotSpot= { -offsetX, -offsetY };
755	cursor= [[NSCursor alloc] initWithImage: image hotSpot: hotSpot];
756      }
757      [cursor set];
758      [pool release];
759      [view unlockFocus];
760    }
761  return 1;
762}
763
764#if 0
765static sqInt display_ioSetCursor(sqInt cursorBitsIndex, sqInt offsetX, sqInt offsetY)
766{
767  return ioSetCursorWithMask(cursorBitsIndex, cursorBitsIndex, offsetX, offsetY);
768}
769#endif
770
771
772static sqInt display_ioForceDisplayUpdate(void)
773{
774  return 0;
775}
776
777
778#if 0
779
780static void setRects(int w, int h)
781{
782  debugf(("setRects %d %d\n", w, h));
783  topRect= NSMakeRect(0,0, w,h);
784  if (fullscreen)
785    {
786      titleRect=  NSMakeRect(0, dpyHeight, dpyWidth, 0);
787      resizeRect= NSMakeRect(dpyWidth, 0, 0, 0);
788    }
789  else
790    {
791      void *port= [view qdPort];
792      titleRect  = [NSWindow frameRectForContentRect: topRect styleMask: styleMask];
793      titleRect.origin.y += h;
794      titleRect.size.height -= h;
795      resizeRect= NSMakeRect(pixWidth - RESIZE_EXTENT, 0, RESIZE_EXTENT, RESIZE_EXTENT);
796      if (port)	// no port while window is deferred
797	{
798	  PixMapHandle pix;
799	  LockPortBits(port);
800	  {
801	    pix      = GetPortPixMap(port);
802	    pixPitch = GetPixRowBytes(pix);
803	    pixBase  = ((char *)GetPixBaseAddr(pix)
804			+ ((int)titleRect.size.height * pixPitch));
805	  }
806	  UnlockPortBits(port);
807	}
808    }
809  setSavedWindowSize((w << 16) | h);	// assume this is atomic
810}
811
812#endif
813
814
815static char *updatePix(void)
816{
817  void *port= [view qdPort];
818  assert(win);  assert(topView);  assert(view);
819  if (port)	// no port while window is deferred
820    {
821      int w, h;
822      NSRect winRect= [win frame];
823      winRect.origin= NSMakePoint(0, 0);	// window coordinates
824      topRect= [NSWindow contentRectForFrameRect: winRect styleMask: styleMask];
825      w= NSWidth(topRect);
826      h= NSHeight(topRect);
827      debugf(("updatePix w=%d h=%d\n", w, h));
828      setSavedWindowSize((w << 16) | h);			// assume this is atomic
829      if (fullscreen)
830	{
831	  titleRect=  NSMakeRect(0, dpyHeight, dpyWidth, 0);	// empty & offscreen
832	  resizeRect= NSMakeRect(dpyWidth, 0, 0, 0);		// empty & offscreen
833	}
834      else
835	{
836	  titleRect= winRect;
837	  titleRect.origin.y    += h;
838	  titleRect.size.height -= h;
839	  resizeRect= NSMakeRect(w - RESIZE_EXTENT, 0, RESIZE_EXTENT, RESIZE_EXTENT);
840	}
841      pixWidth= w;
842      pixHeight= h;
843      LockPortBits(port);
844      {
845	PixMapHandle pix= GetPortPixMap(port);
846	pixDepth= GetPixDepth(pix);
847	pixPitch= GetPixRowBytes(pix);
848	assert(pixPitch);
849	assert(pixPitch >= w * (pixDepth / 8));
850	pixBase= ((char *)GetPixBaseAddr(pix) + ((int)NSHeight(titleRect) * pixPitch));
851	assert(pixBase);
852      }
853      UnlockPortBits(port);
854    }
855  else
856    {
857      debugf(("updatePix: NO PORT!\n"));
858      pixBase= 0;
859    }
860  debugf(("pixBase %p, width %d, height %d, depth %d, pitch %d\n", pixBase, pixWidth, pixHeight, pixDepth, pixPitch));
861  return pixBase;
862}
863
864
865
866#define bytesPerLine(width, depth)	((((width)*(depth) + 31) >> 5) << 2)
867
868static sqInt display_ioShowDisplay(sqInt dispBitsIndex, sqInt width, sqInt height, sqInt depth,
869				   sqInt affectedL, sqInt affectedR, sqInt affectedT, sqInt affectedB)
870{
871  int affectedW, affectedH;
872
873  if (headless
874      || (width != pixWidth) || (width < 1) || (height != pixHeight) || (height < 1) || (depth != pixDepth)
875      || ((!pixBase) && !updatePix())
876      || (displayChanged)
877      || (![view lockFocusIfCanDraw]))
878    {
879      debugf(("ioShowDisplay squashed: dpy %dx%dx%d pix %dx%dx%d\n",
880	      (int)width, (int)height, (int)depth,
881	      (int)pixWidth, (int)pixHeight, (int)pixDepth));
882      return 0;
883    }
884
885  debugf(("ioShowDisplay %p %ldx%ldx%ld %ld,%ld-%ld,%ld\n",
886	  (void *)dispBitsIndex, width, height, depth,
887	  affectedL, affectedR, affectedT, affectedB));
888
889  lock(display);
890  affectedR= min(affectedR, min(width,  pixWidth ));
891  affectedB= min(affectedB, min(height, pixHeight));
892  affectedW= affectedR - affectedL;
893  affectedH= affectedB - affectedT;
894  if ((affectedW > 0) && (affectedH > 0))
895    {
896      int   opp=	depth / 8;	// octets per pixel
897      char *out=	pixBase;
898      int   outPitch=	pixPitch;
899      void *port=	[view qdPort];
900      LockPortBits(port);
901      //xxx FIXME SOON: cope with dpy depth mismatch (share the code
902      // used by the other types of Unix display)
903      {
904	int   pitch= bytesPerLine(width, depth);
905	char *in=    pointerForOop(dispBitsIndex) + affectedL * opp + affectedT * pitch;
906	int   lines= affectedH;
907	int   bytes= affectedW * opp;
908
909	out += (affectedL * opp) + (affectedT * outPitch);
910
911	if ((bytes == pitch) && (bytes == outPitch))
912	  memcpy(out, in, bytes * lines);
913	else if (bytes < 9) // empirical
914	  while (lines--)
915	    {
916	      register char *to=    out;
917	      register char *from=  in;
918	      register int   count= bytes;
919	      while (count--)
920		*to++= *from++;
921	      in  += pitch;
922	      out += outPitch;
923	    }
924	else
925	  while (lines--)
926	    {
927	      memcpy((void *)out, (void *)in, bytes);
928	      in  += pitch;
929	      out += outPitch;
930	    }
931      }
932      SetRectRgn(pixRegion, affectedL, affectedT, affectedR, affectedB);
933      QDFlushPortBuffer([view qdPort], pixRegion);
934      UnlockPortBits(port);
935    }
936  unlock(display);
937  [view unlockFocus];
938
939  return 0;
940}
941
942
943#if 0
944
945static void display_ioFlushDisplay(void)
946{
947  void *port;
948  lock(display);
949  port= [view qdPort];
950  LockPortBits(port);
951  SetRectRgn(pixRegion, 0, 0, pixWidth, pixHeight);
952  QDFlushPortBuffer([view qdPort], pixRegion);
953  UnlockPortBits(port);
954  unlock(display);
955}
956
957#endif
958
959static sqInt display_ioHasDisplayDepth(sqInt i)
960{
961  return i == (headless ? 1 : dpyDepth);
962}
963
964static sqInt display_ioSetDisplayMode(sqInt width, sqInt height, sqInt depth, sqInt fullscreenFlag)
965{
966  if (headless)
967    return 0;
968
969  printf("ioSetDisplayMode: IMPLEMENT ME\n");
970  return ((width == dpyWidth) && (height == dpyHeight) && (depth == dpyDepth));
971}
972
973
974static void *display_ioGetDisplay(void)
975{
976  if (headless)
977    return 0;
978
979  debugf(("ioGetDisplay: WARNING: check the client to see it knows what it's doing\n"));
980  return dpy;
981}
982
983static void *display_ioGetWindow(void)
984{
985  if (headless)
986    return 0;
987
988  printf("ioGetWindow: WARNING: check the client to see it knows what it's doing\n");
989  return 0;
990}
991
992static sqInt display_clipboardWriteFromAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex)
993{
994  NSAutoreleasePool *pool=   [[NSAutoreleasePool alloc] init];
995  NSPasteboard      *pboard= [NSPasteboard generalPasteboard];
996  char		    *buf= malloc(count * 2);
997  int		     len= sq2uxText(pointerForOop(byteArrayIndex) + startIndex, count, buf, count * 2, 1);
998  NSString	    *string= [NSString stringWithCString: buf length: len];
999  [pboard declareTypes: [NSArray arrayWithObject: NSStringPboardType] owner: nil];
1000  [pboard setString: string forType: NSStringPboardType];
1001  free(buf);
1002  [pool release];
1003  return 0;
1004}
1005
1006
1007static sqInt display_clipboardSize(void)
1008{
1009  NSAutoreleasePool *pool=   [[NSAutoreleasePool alloc] init];
1010  NSPasteboard      *pboard= [NSPasteboard generalPasteboard];
1011  NSString          *type=   [pboard availableTypeFromArray:
1012				       [NSArray arrayWithObject:
1013						  NSStringPboardType]];
1014  int clipSize= 0;
1015  if (clipboard)
1016    free(clipboard);
1017  clipboard= 0;
1018  if (type != nil)
1019    {
1020      NSString *contents= [pboard stringForType: type];
1021      if (contents != nil)
1022	{
1023	  const char *cString= [contents cString];
1024	  int len= [contents length];
1025	  if (len)
1026	    {
1027	      clipboard= (char *)malloc(len * 2);
1028	      if (!clipboard)
1029		fprintf(stderr, "could not allocate clipboard\n");
1030	      else
1031		clipSize= ux2sqText((char *)cString, len, clipboard, len * 2, 1);
1032	    }
1033	}
1034    }
1035  [pool release];
1036  return clipSize;
1037}
1038
1039static sqInt display_clipboardReadIntoAt(sqInt count, sqInt byteArrayIndex, sqInt startIndex)
1040{
1041  if (clipboard)
1042    {
1043      memcpy(pointerForOop(byteArrayIndex) + startIndex, clipboard, count);
1044      return count;
1045    }
1046  return 0;
1047}
1048
1049static char **display_clipboardGetTypeNames(void)
1050{
1051  return 0;
1052};
1053
1054static sqInt display_clipboardSizeWithType(char *typeName, int ntypeName)
1055{
1056  return 0;
1057}
1058
1059static void display_clipboardWriteWithType(char *data, size_t nData, char *typeName, size_t nTypeName, int isDnd, int isClaiming)
1060{
1061  return;
1062}
1063
1064static sqInt display_dndOutStart(char *types, int ntypes)	{ return 0; }
1065static void  display_dndOutSend(char *bytes, int nbytes)	{ return  ; }
1066static sqInt display_dndOutAcceptedType(char * buf, int nbuf)	{ return 0; }
1067
1068static void display_winExit(void)
1069{
1070  [win close];
1071}
1072
1073
1074static void display_winSetName(char *title)
1075{
1076  char *base= strrchr(title, '/');
1077  if (base) title= base + 1;
1078#if (RESIZE_IN_TITLE)
1079  if (showExtent)
1080    {
1081      char buf[NAME_MAX];
1082      NSRect frame= [view frame];
1083      sprintf(buf, "%s (%dx%d)", title, (int)NSWidth(frame), (int)NSHeight(frame));
1084      title= buf;
1085    }
1086#endif
1087  [win setTitle: [NSString stringWithCString: title]];
1088}
1089
1090
1091static void  display_parseEnvironment(void) {}
1092
1093static int display_parseArgument(int argc, char **argv)
1094{
1095  if     (!strncmp(*argv, "-psn_", 5))		return fromFinder= 1;
1096  else if (!strcmp(*argv, "-quartz"))		return 1;
1097  else if (!strcmp(*argv, "-fullscreen"))	return fullscreen= 1;
1098  else if (!strcmp(*argv, "-headless"))		return headless= 1;
1099  else if (!strcmp(*argv, "-notitle"))		return noTitle= 1;
1100  else if (!strcmp(*argv, "-nodock"))		return noDock= 1;
1101  else if (!strcmp(*argv, "-swapbtn"))		return swapBtn= 1;
1102  return 0;
1103}
1104
1105static void display_printUsage(void)
1106{
1107  printf("\nQuartz/Aqua <option>s:\n");
1108  printf("  -fullscreen           occupy the entire screen\n");
1109  printf("  -headless             run in headless (no window) mode\n");
1110  printf("  -nodock               don't show Squeak in the dock\n");
1111  printf("  -notitle              disable the Squeak window title bar\n");
1112  printf("  -swapbtn              swap mouse buttons 2 (yellow) and 3 (blue)\n");
1113}
1114
1115static void display_printUsageNotes(void)
1116{
1117  printf("  -nodock is only useful with `-headless'.\n");
1118}
1119
1120
1121///
1122/// window initialization
1123///
1124
1125
1126static void menuAddItem(NSMenu *menu, NSString *title, SEL action,
1127			NSString *key, int opt)
1128{
1129  NSMenuItem *item= [[NSMenuItem alloc]
1130		      initWithTitle: title
1131		      action:        action
1132		      keyEquivalent: (cmdKeys ? key : @"")];
1133  [menu addItem: item];
1134  if (opt)
1135    [item setKeyEquivalentModifierMask: (NSCommandKeyMask | NSAlternateKeyMask)];
1136  [item release];
1137}
1138
1139static void installMenu(SEL install, NSMenu *menu)
1140{
1141  NSMenuItem *item= [[NSMenuItem alloc]
1142		      initWithTitle: @""
1143		      action:	 nil
1144		      keyEquivalent: @""];
1145  [item setSubmenu: menu];
1146  [[NSApp mainMenu] addItem: item];
1147  if (install != NULL)
1148    {
1149      extern id objc_msgSend(id theReceiver, SEL theSelector, ...);
1150      objc_msgSend(NSApp, install, menu);
1151    }
1152  [item release];
1153  [menu release];
1154}
1155
1156
1157// InterfaceBuilder?  Just Say No!
1158
1159static void setUpMenus(void)
1160{
1161  if (headless && noDock)
1162    return;
1163
1164  [NSApp setMainMenu: [[NSMenu alloc] init]];
1165  {
1166    NSMenu *menu= [[NSMenu alloc] initWithTitle: @"Squeak"];
1167    menuAddItem(menu, @"About Squeak",   @selector(performAbout:),          @"",  0);
1168    [menu addItem: [NSMenuItem separatorItem]];
1169    menuAddItem(menu, @"Preferences...", @selector(performPreferences:),    @"y", 0);
1170    [menu addItem: [NSMenuItem separatorItem]];
1171    menuAddItem(menu, @"Hide Squeak",    @selector(hide:),                  @"h", 0);
1172    menuAddItem(menu, @"Hide Others",    @selector(hideOtherApplications:), @"h", 1);
1173    menuAddItem(menu, @"Show All",       @selector(unhideAllApplications:), @"",  0);
1174    [menu addItem: [NSMenuItem separatorItem]];
1175    menuAddItem(menu, @"Quit Squeak",    @selector(terminate:),             @"q", 0);
1176    installMenu(@selector(setAppleMenu:), menu);
1177  }
1178  {
1179    NSMenu *menu= [[NSMenu alloc] initWithTitle: @"File"];
1180    menuAddItem(menu, @"Page Setup...", @selector(performPageSetup:), @"P", 0);
1181    menuAddItem(menu, @"Print",         @selector(performPrint:),     @"p", 0);
1182    installMenu(NULL, menu);
1183  }
1184  {
1185    NSMenu *menu= [[NSMenu alloc] initWithTitle: @"Window"];
1186    menuAddItem(menu, @"Minimise", @selector(performMiniaturize:), @"m", 0);
1187    if (cmdKeys)
1188      menuAddItem(menu, @"Disable Command Keys", @selector(performDisableKeys:), @"k", 0);
1189    else
1190      menuAddItem(menu, @"Enable Command Keys",  @selector(performEnableKeys:),  @"",  0);
1191    installMenu(@selector(setWindowsMenu:), menu);
1192  }
1193  {
1194    NSMenu *menu= [[NSMenu alloc] initWithTitle: @"Help"];
1195    menuAddItem(menu, @"Squeak Help", @selector(showHelp:), @"?", 0);
1196    installMenu(NULL, menu);
1197  }
1198}
1199
1200
1201#include "CPS.h"
1202
1203static char *str4(UInt32 chars)
1204{
1205  static char str[5];
1206  *(int *)&str= chars;
1207  str[4]= '\0';
1208  return str;
1209}
1210
1211static void setUpDock(void)
1212{
1213  // this was passed to us in argv, but we have to pick it up from CPS
1214  // anyway if the VM was started from a command line or script
1215  CPSProcessSerNum psn;
1216  OSErr err;
1217
1218  if (headless && noDock)
1219    return;
1220
1221# define try(FN, ARGS, CAVEAT)						\
1222    if ((err= FN ARGS)) fprintf(stderr, "%s: error %d%s\n", #FN, err, CAVEAT)
1223
1224  try(CPSGetCurrentProcess, (&psn), "");
1225  else try(CPSSetProcessName, (&psn, "Squeak"), "");
1226  else
1227    {
1228      CPSEnableForegroundOperation(&psn, 0x03, 0x3c, 0x2c, 0x1103);
1229      try(CPSSetFrontProcess, (&psn), "");
1230    }
1231# undef try
1232# if defined(DEBUG_APP)
1233  {
1234    CPSProcessInfoRec info;
1235    char path[4096];
1236    int  len;
1237    char name[4096];
1238    CPSGetProcessInfo(&psn, &info, path, sizeof(path), &len, name, sizeof(name));
1239    printf("process:\n");
1240    printf("  pid:     %d\n", info.UnixPID);
1241    printf("  path:    %s\n", path);
1242    printf("  name:    %s\n", name);
1243    printf("  creator: %s\n", str4(info.ExecFileCreator));
1244    printf("  type:    %s\n", str4(info.ExecFileType));
1245    printf("  flavour: ");
1246    switch(info.Flavour)
1247      {
1248      case kCPSBlueApp:	   printf("BlueApp\n"); break;
1249      case kCPSBlueBox:	   printf("BlueBox\n"); break;
1250      case kCPSCarbonApp:  printf("Carbon\n"); break;
1251      case kCPSYellowApp:  printf("YellowApp\n"); break;
1252      case kCPSUnknownApp: printf("unknown\n"); break;
1253      }
1254    printf("  attrs:   %d", info.Attributes);
1255    if (info.Attributes & kCPSBGOnlyAttr)	printf(" BGOnly");
1256    if (info.Attributes & kCPSUIElementAttr)	printf(" UIElement");
1257    if (info.Attributes & kCPSHiddenAttr)	printf(" Hidden");
1258    if (info.Attributes & kCPSNoConnectAttr)	printf(" NoConnect");
1259    if (info.Attributes & kCPSFullScreenAttr)	printf(" FullScreen");
1260    if (info.Attributes & kCPSClassicReqAttr)	printf(" ClassicReq");
1261    if (info.Attributes & kCPSNativeReqAttr)	printf(" NativeReq");
1262    printf("\n");
1263  }
1264#endif
1265}
1266
1267
1268static char *display_winSystemName(void)
1269{
1270  return "Quartz";
1271}
1272
1273
1274static void display_winInit(void)
1275{
1276  [[NSAutoreleasePool alloc] init];
1277  [Squeak sharedApplication];
1278  [NSApp setDelegate: NSApp];
1279  // from winOpen()...
1280  setUpMenus();
1281  setUpDock();
1282  [NSApp run];
1283}
1284
1285
1286static void display_winOpen(void) {}
1287
1288
1289static void setUpDisplay(void)
1290{
1291  if (headless)
1292    return;
1293
1294  if (!dpy)
1295    pixRegion= NewRgn();
1296
1297  dpy        = kCGDirectMainDisplay;
1298  dpyMode    = (NSDictionary *)CGDisplayCurrentMode(dpy);
1299  dpyWidth   = [[dpyMode objectForKey: (id)kCGDisplayWidth] intValue];
1300  dpyHeight  = [[dpyMode objectForKey: (id)kCGDisplayHeight] intValue];
1301  dpyDepth   = [[dpyMode objectForKey: (id)kCGDisplayBitsPerPixel] intValue];
1302  dpyPixels  = CGDisplayBaseAddress(dpy);
1303  dpyPitch   = CGDisplayBytesPerRow(dpy);
1304
1305  debugf(("display is %dx%dx%d at %p pitch %d\n", dpyWidth, dpyHeight, dpyDepth, dpyPixels, dpyPitch));
1306}
1307
1308
1309static void setUpWindow(int fs)
1310{
1311  if (!headless)
1312    {
1313      int w, h;
1314      NSRect contentRect;
1315      if (fs)
1316	{
1317	  setUpDisplay();
1318	  w= dpyWidth;
1319	  h= dpyHeight;
1320	}
1321      else
1322	{
1323	  int winSize= getSavedWindowSize();
1324	  if (winSize)
1325	    {
1326	      w= winSize >> 16;
1327	      h= winSize & 0xffff;
1328	    }
1329	  else
1330	    {
1331	      w= 640;
1332	      h= 480;
1333	    }
1334	}
1335      debugf(("initial winSize %d %d\n", w, h));
1336      styleMask= (fs
1337		  ? (NSBorderlessWindowMask)
1338		  : (  NSTitledWindowMask
1339		     | NSMiniaturizableWindowMask
1340		     | NSResizableWindowMask));
1341      //xxx does quartz _really_ have _no_ mechanism to set window bit gravity?!?
1342      win= [[SqueakWindow alloc]
1343	     initWithContentRect: NSMakeRect(0,0, w,h)
1344	     styleMask:           styleMask
1345	     backing:             NSBackingStoreBuffered
1346	     defer:               NO];
1347
1348      contentRect= [[win contentView] frame];
1349      w= NSWidth(contentRect);
1350      h= NSHeight(contentRect);
1351      debugf(("alloc winSize %d %d\n", w, h));
1352      setSavedWindowSize((w << 16) | h);
1353
1354      view= [[SqueakView alloc] initWithFrame: contentRect];
1355      [view setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)];
1356
1357      [win setReleasedWhenClosed: YES];
1358      [win setAcceptsMouseMovedEvents: YES];
1359      [win setShowsResizeIndicator: NO];
1360
1361      topView= [[TopView alloc] initWithFrame: contentRect];
1362
1363      [win setInitialFirstResponder: view];
1364      [win setDelegate: NSApp];
1365      [win useOptimizedDrawing: YES];
1366
1367      //      [win setBackgroundColor: [NSColor clearColor]];
1368      //      [win setAlphaValue: 1.0];
1369      //      [win setOpaque: YES];
1370      //      [win setAutodisplay: YES];
1371      //[win disableFlushWindow];
1372
1373      if (fs)
1374	[win setLevel: CGShieldingWindowLevel()];
1375      else
1376	{
1377	  [win center];
1378	  if (!fromFinder)
1379	    [win setIcon];
1380	  display_winSetName(shortImageName);
1381	}
1382
1383      [topView addSubview: view];	//[view release];
1384      [win setContentView: topView];	//[topView release];
1385      [win makeKeyAndOrderFront: nil];	// need platform window to get pixBase
1386    }
1387}
1388
1389
1390static int		  imageWidth;
1391static int		  imageHeight;
1392static char		 *imageData;
1393static CGDataProviderRef  imageDataProvider;
1394static CGImageRef	  imageRef;
1395
1396static void cgdpRelease(void *info, const void *data, size_t size) {}
1397
1398static void captureImage(int inverted)
1399{
1400  imageWidth=  pixWidth;
1401  imageHeight= pixHeight;
1402  imageData=   (char *)malloc(pixPitch * imageHeight);
1403  if (inverted)
1404    {
1405      char *in= pixBase, *out= imageData + ((imageHeight - 1) * pixPitch);
1406      int   y;
1407      for (y= pixHeight;  y--;  (in += pixPitch), (out -= pixPitch))
1408	memcpy(out, in, pixPitch);
1409    }
1410  else
1411    {
1412      memcpy(imageData, pixBase, imageHeight * pixPitch);
1413    }
1414  imageDataProvider=
1415    CGDataProviderCreateWithData(0, imageData, pixPitch*imageHeight,
1416				 cgdpRelease);
1417  imageRef=
1418    CGImageCreate(imageWidth, imageHeight, 8, 32, pixPitch,
1419		  CGColorSpaceCreateDeviceRGB(),
1420		  kCGImageAlphaNoneSkipFirst,
1421		  imageDataProvider, 0, 0,
1422		  kCGRenderingIntentDefault);
1423}
1424
1425
1426static void drawImage(CGContextRef cgc, int offset)
1427{
1428  CGContextDrawImage(cgc, CGRectMake(0, offset, imageWidth, imageHeight), imageRef);
1429}
1430
1431
1432static void releaseImage(int malloced)
1433{
1434  CGImageRelease(imageRef);
1435  CGDataProviderRelease(imageDataProvider);
1436  if (malloced)
1437    free(imageData);
1438}
1439
1440
1441#ifdef FULLSCREEN_FADE
1442
1443// YES, I know there's a CG API to do this.  But it sucks.
1444
1445static struct
1446{
1447  CGGammaValue r[256], g[256], b[256];
1448} dpyGamma;
1449
1450static void fadeOut(float delta)
1451{
1452  CGGammaValue r[256], g[256], b[256];
1453  int sz;
1454
1455  if ((CGDisplayNoErr == CGGetDisplayTransferByTable
1456			   (dpy, 256, dpyGamma.r, dpyGamma.g, dpyGamma.b, &sz))
1457      && (256 == sz))
1458    {
1459      float scale;
1460      memcpy(r, dpyGamma.r, sizeof(r));
1461      memcpy(g, dpyGamma.g, sizeof(g));
1462      memcpy(b, dpyGamma.b, sizeof(b));
1463      for (scale= 1.0;  scale >= 0.0;  scale -= delta)
1464	{
1465	  int i;
1466	  for (i= 256;  i--;)
1467	    {
1468	      r[i]= dpyGamma.r[i] * scale;
1469	      g[i]= dpyGamma.g[i] * scale;
1470	      b[i]= dpyGamma.b[i] * scale;
1471	    }
1472	  if (CGDisplayNoErr != CGSetDisplayTransferByTable(dpy, 256, r, g, b))
1473	    {
1474	      printf("failed to set transfer table\n");
1475	      CGDisplayRestoreColorSyncSettings();
1476	      return;
1477	    }
1478	  usleep(10000);
1479	}
1480    }
1481  else
1482    {
1483      printf("failed to get display transfer table (%d)\n", sz);
1484    }
1485}
1486
1487static void fadeIn(float delta)
1488{
1489  CGGammaValue r[256], g[256], b[256];
1490  float scale;
1491  memset(r, 0, sizeof(r));
1492  memset(g, 0, sizeof(g));
1493  memset(b, 0, sizeof(b));
1494  for (scale= 0.0;  scale <= 1.0;  scale += delta)
1495    {
1496      int i;
1497      for (i= 256; i--;)
1498	{
1499	  r[i] = dpyGamma.r[i] * scale;
1500	  g[i] = dpyGamma.g[i] * scale;
1501	  b[i] = dpyGamma.b[i] * scale;
1502	}
1503      if (CGDisplayNoErr != CGSetDisplayTransferByTable(dpy, 256, r, g, b))
1504	break;
1505      usleep(10000);
1506    }
1507  CGDisplayRestoreColorSyncSettings();
1508}
1509
1510#endif
1511
1512
1513#if 1
1514
1515static sqInt display_ioSetFullScreen(sqInt flag)
1516{
1517  static sqInt originalWindowSize= 0;
1518  SqueakWindow *old;
1519
1520  debugf(("ioSetFullScreen(%d)\n", flag));
1521
1522  if (headless || (fullscreen == flag))
1523    return 0;	// nothing to do
1524  old= win;
1525  win= 0;  view= 0;  topView= 0;  pixBase= 0; pixWidth= 0; pixHeight= 0; pixPitch= 0;
1526  if (flag)
1527    originalWindowSize= getSavedWindowSize();
1528  else if (originalWindowSize)
1529    setSavedWindowSize(originalWindowSize);
1530  setFullScreenFlag(fullscreen= flag);
1531  setUpWindow(flag);
1532  reframeRenderers();
1533  [old close];
1534  return 1;
1535}
1536
1537#else
1538
1539static sqInt display_ioSetFullScreen(sqInt flag)
1540{
1541  static sqInt originalWindowSize= (800 << 16) | 600;
1542
1543  debugf(("ioSetFullScreen(%d)\n", flag));
1544
1545  if (headless || (fullscreen == flag) || glActive)
1546    return 0;	// nothing to do
1547
1548  if (flag)	// switch to fullscreen
1549    {
1550      CGDisplayHideCursor(dpy);
1551#    ifdef FULLSCREEN_FADE
1552      captureImage(0);
1553      fadeOut(FULLSCREEN_FADE);
1554#    endif
1555      if (CGDisplayNoErr != CGDisplayCapture(dpy))
1556	debugf(("failed to capture display\n"));
1557      else
1558	{
1559#        ifdef FULLSCREEN_FADE
1560	  CGContextRef cgc;
1561	  memset(dpyPixels, -1U, dpyPitch * dpyHeight);
1562	  cgc= CGBitmapContextCreate(dpyPixels, dpyWidth, dpyHeight,
1563				     8, dpyPitch,
1564				     CGColorSpaceCreateDeviceRGB(),
1565				     kCGImageAlphaNoneSkipFirst);
1566	  drawImage(cgc, dpyHeight - pixHeight);
1567	  CGContextRelease(cgc);
1568#	 endif
1569	  lock(display);
1570	  originalWindowSize= getSavedWindowSize();
1571	  pixWidth=   dpyWidth;
1572	  pixHeight=  dpyHeight;
1573	  fullscreen= 1;
1574	  updatePix();
1575	  unlock(display);
1576	  [NSMenu setMenuBarVisible: NO];
1577	}
1578#    ifdef FULLSCREEN_FADE
1579      fadeIn(FULLSCREEN_FADE);
1580      releaseImage(0);
1581#    endif
1582      mousePosition.x= mousePosition.y= -1;
1583      CGDisplayShowCursor(dpy);
1584    }
1585  else		// switch to windowed
1586    {
1587#    ifdef FULLSCREEN_FADE
1588      fadeOut(FULLSCREEN_FADE);
1589#    endif
1590      [NSMenu setMenuBarVisible: YES];
1591      CGDisplayRelease(dpy);
1592      fullscreen= 0;
1593      lock(display);
1594      setSavedWindowSize(originalWindowSize);
1595      pixWidth=  originalWindowSize >> 16;
1596      pixHeight= originalWindowSize & 0xffff;
1597      updatePix();
1598      unlock(display);
1599#    ifdef FULLSCREEN_FADE
1600      fadeIn(FULLSCREEN_FADE);
1601#    endif
1602    }
1603
1604  return 1;
1605}
1606
1607#endif
1608
1609
1610
1611@implementation Squeak
1612
1613
1614+ (void) initialize
1615{
1616  NSMutableDictionary *dict;
1617  NSUserDefaults *defaults;
1618
1619  defaults= [NSUserDefaults standardUserDefaults];
1620  dict= [NSMutableDictionary dictionary];
1621
1622  [dict setObject: @"YES" forKey: @"AppleDockIconEnabled"];
1623  [defaults registerDefaults: dict];
1624}
1625
1626
1627static char *documentName= 0;
1628
1629
1630-(BOOL) application: (NSApplication *) theApplication
1631	openFile:    (NSString *)      filename
1632{
1633  if (fromFinder)
1634    documentName= strdup([filename cString]);
1635  return YES;
1636}
1637
1638
1639#if 0 // only for running with increased stack size
1640static void *runInterpreter(void *arg)
1641{
1642  [(id)arg interpret: nil];
1643}
1644#endif
1645
1646
1647-(void) applicationDidFinishLaunching: (NSNotification *)note
1648{
1649  int fds[2];
1650
1651  // this saves an awful lot of tedious mutex contention (and besides
1652  // is essentially free, since there's no way to avoid writing a
1653  // socket to inform aio of the availability of the event)
1654#if 0
1655  if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0)
1656    {
1657      perror("socketpair");
1658      exit(1);
1659    }
1660  osXfd= fds[0];
1661  stXfd= fds[1];
1662#else
1663  if (pipe(fds))
1664    {
1665      perror("pipe");
1666      exit(1);
1667    }
1668  stXfd= fds[0];
1669  osXfd= fds[1];
1670#endif
1671  aioEnable(stXfd, 0, 0);
1672  aioHandle(stXfd, evtHandler, AIO_RX);
1673#if (!USE_SPINLOCK)
1674  {
1675    pthread_mutexattr_t attr;
1676    pthread_mutexattr_init(&attr);
1677#  ifndef NDEBUG
1678    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
1679#  endif
1680    if (pthread_mutex_init(&displayMx, &attr))
1681      {
1682	perror("pthread_mutex_init");
1683	exit(1);
1684      }
1685    pthread_mutexattr_destroy(&attr);
1686  }
1687#endif
1688
1689  if (fromFinder)
1690    {
1691      char *ptr= 0;
1692      strncpy(resourcePath, argVec[0], sizeof(resourcePath));	// .app/Contents/MacOS/squeak
1693      if ((ptr= strrchr(resourcePath, '/')))
1694	{
1695	  *ptr= '\0';						// .app/Contents/MacOS
1696	  if ((ptr= strrchr(resourcePath, '/')))
1697	    {
1698	      *ptr= '\0';						// .app/Contents
1699	      strcpy(ptr, "/Resources/");				// .app/Contents/Resources/
1700	    }
1701	  else
1702	    resourcePath[0]= '\0';
1703	}
1704      else
1705	resourcePath[0]= '\0';
1706    }
1707
1708  imgInit();
1709  setUpDisplay();
1710  setUpWindow(fullscreen= getFullScreenFlag());
1711
1712#if 1
1713  [NSThread
1714    detachNewThreadSelector: @selector(interpret:)
1715    toTarget:		     self
1716    withObject:		     nil];
1717#else
1718  // ensure cocoa is initialised for threads
1719  {
1720    id obj= [NSObject new];
1721    [NSThread detachNewThreadSelector: @selector(self) toTarget: obj withObject: nil];
1722    [obj release];
1723  }
1724  // run interpreter with stack size > default
1725  {
1726    pthread_t	   thread;
1727    pthread_attr_t attr;
1728    pthread_attr_init(&attr);
1729    pthread_attr_setstacksize(&attr, 8192*1024);
1730    pthread_create(&thread, &attr, runInterpreter, (void *)self);
1731  }
1732#endif
1733}
1734
1735
1736- (void) interpret: (id)context
1737{
1738  [[NSAutoreleasePool alloc] init];	// running in new thread
1739  interpret();
1740  (void)recordMouseEvent;
1741  (void)recordKeyboardEvent;
1742  (void)recordDragEvent;
1743  (void)recordWindowEvent;
1744}
1745
1746
1747- (void) applicationDidChangeScreenParameters: (NSNotification *)note
1748{
1749  //xxx this one might be tricky in the absence of appWillChangeScreenParams:
1750  fprintf(stderr, "\nDISPLAY PARAMETERS CHANGED\n\n");
1751  //  lock(display);
1752  pixWidth= pixHeight= pixDepth= 0;
1753  setUpDisplay();
1754  //setUpWindow(getFullScreenFlag());
1755  updatePix();
1756  //  unlock(display);
1757  //setUpMenus();
1758  displayChanged= 1;
1759  //fullDisplayUpdate();
1760}
1761
1762
1763- (void) unhideAllApplications: (id)sender
1764{
1765  [super unhideAllApplications: sender];
1766  [win orderFront: self]; // so that unhinding once more will reveal the Sq window
1767}
1768
1769
1770- (BOOL) windowShouldClose: (id)sender
1771{
1772  return NO;
1773}
1774
1775
1776- (void) terminate: (id)sender
1777{
1778  [super terminate: sender];
1779  exit(0);
1780}
1781
1782- (void) maybeTerminate: (id)sender
1783{
1784  switch (NSRunAlertPanel(@"Really quit?",
1785			  @"All changes since your last save will be lost.\n\nIf you want to save your changes, press `Cancel' and then choose `save and quit' from the background menu in the Squeak window.",
1786			  @"Quit",
1787			  @"Cancel",
1788			  nil))
1789    {
1790    case NSAlertDefaultReturn:	[self terminate: self];
1791    }
1792}
1793
1794
1795- (void) performAbout: (id)sender
1796{
1797  extern char *getVersionInfo(int verbose);
1798  char *info= getVersionInfo(1);
1799  NSPanel *panel= NSGetInformationalAlertPanel(@"About Squeak",
1800					       @"%s",
1801					       @"Dismiss",
1802					       nil,
1803					       nil,
1804					       info);
1805  NSRect frame= [panel frame];
1806  frame.size.width *= 1.5;
1807  [panel setFrame: frame display: NO];
1808  [NSApp runModalForWindow: panel];
1809  [panel close];
1810  free(info);
1811}
1812
1813
1814
1815//xxx why does rebuilding the menu lose boldface on the Apple menu item???
1816
1817- (void) performEnableKeys:  (id)sender	{ cmdKeys= 1;  setUpMenus(); }
1818- (void) performDisableKeys: (id)sender	{ cmdKeys= 0;  setUpMenus(); }
1819
1820
1821- (void) windowWillMove: (NSNotification *)note
1822{
1823  //xxx FIXME SOON: there are other ways to enter this (and ways other than
1824  // noteEvent to escape from it)
1825  inModalLoop= 1;
1826}
1827
1828
1829- (NSSize) windowWillResize: (NSWindow *)sender toSize: (NSSize)size
1830{
1831  return glActive ? [sender frame].size : size;
1832}
1833
1834
1835- (void) windowDidResize: (NSNotification *)note
1836{
1837  reframeRenderers();
1838}
1839
1840
1841-(void) sendEvent: (NSEvent *)event
1842{
1843  int	  type=     [event type];
1844  NSPoint loc=      [event locationInWindow];
1845  NSWindow *evtWin= [event window];
1846#if 0
1847  NSPoint loc=      (fullscreen
1848		     ? [NSEvent mouseLocation]	//xxx should use deltas
1849		     : [event locationInWindow]);
1850#endif
1851
1852  if (evtWin && ((NSWindow *)win != [event window]))
1853    {
1854      //printf("evtWin not local\n");
1855      [super sendEvent: event];
1856      return;
1857    }
1858
1859  switch (type)
1860    {
1861#     define down buttonState |= qz2sqButton([event buttonNumber])
1862#     define move
1863#     define up	  buttonState &= ~qz2sqButton([event buttonNumber])
1864
1865#     define recordEvent(delta)						\
1866      if (fullscreen || NSPointInRect(loc, [view frame]))		\
1867	{								\
1868	  noteMousePoint(loc);						\
1869	  delta;							\
1870	  modifierState= qz2sqModifiers([event modifierFlags]);		\
1871	  noteMouseEvent();						\
1872	}								\
1873      else								\
1874	{								\
1875          /* printf("recordEvent fullscreen %d inRect %d\n", fullscreen, NSPointInRect(loc, [view frame])); */  \
1876	  [super sendEvent: event];	/* don't track outside window */ \
1877        }
1878
1879    case NSLeftMouseDown: case NSOtherMouseDown: case NSRightMouseDown:
1880      if ((!active) || NSPointInRect(loc, resizeRect))
1881	{
1882	  //printf("evt down active %d inRect %d\n", active, NSPointInRect(loc, resizeRect));
1883	  [super sendEvent: event];	// first click, or start resize
1884	}
1885      else
1886	recordEvent(down);
1887      break;
1888
1889    case NSLeftMouseDragged: case NSRightMouseDragged: case NSOtherMouseDragged:
1890      if (!(buttonState & qz2sqButton([event buttonNumber])))
1891	{
1892	  [super sendEvent: event];	// already tracking window move
1893	  break;
1894	}
1895      // fall through...
1896    case NSMouseMoved:
1897      recordEvent(move);
1898      break;
1899
1900    case NSLeftMouseUp: case NSOtherMouseUp: case NSRightMouseUp:
1901      recordEvent(up);
1902      break;
1903
1904#     undef recordEvent
1905#     undef down
1906#     undef move
1907#     undef up
1908
1909    case NSKeyDown:
1910      {
1911	int keyCode;
1912	modifierState= qz2sqModifiers([event modifierFlags]);
1913	keyCode= [view composeKeyDown: event]; //qz2sqKey(event);
1914	if (keyCode >= 0)
1915	  {
1916	    if (cmdKeys)
1917	      {
1918		if ((modifierState == CommandKeyBit) || (modifierState == CommandKeyBit + ShiftKeyBit))
1919		  switch (keyCode)
1920		    {
1921		    case '?': [NSApp showHelp: self];			keyCode= -1; break;
1922		    case 'h': [NSApp hide: self];			keyCode= -1; break;
1923		    case 'k': [NSApp performDisableKeys: self];		keyCode= -1; break;
1924		    case 'm': [win   performMiniaturize: self];		keyCode= -1; break;
1925		    case 'q': [NSApp maybeTerminate: self];		keyCode= -1; break;
1926		    }
1927		else if (modifierState == CommandKeyBit + OptionKeyBit)
1928		  switch (keyCode)
1929		    {
1930		    case 'h': [NSApp hideOtherApplications: self];	keyCode= -1; break;
1931		    }
1932	      }
1933	    if (keyCode >= 0)
1934	      {
1935		if (![event isARepeat])
1936		  noteKeyboardEvent(keyCode, EventKeyDown, modifierState);
1937		noteKeyboardEvent(keyCode, EventKeyChar, modifierState);
1938		recordKeystroke(keyCode);			/* DEPRECATED */
1939	      }
1940	    else // key up not interesting
1941	      [view composeKeyUp: event];
1942	  }
1943      }
1944      break;
1945
1946    case NSKeyUp:
1947      {
1948	int keyCode;
1949	modifierState= qz2sqModifiers([event modifierFlags]);
1950	keyCode= [view composeKeyUp: event]; //qz2sqKey(event);
1951	if (keyCode >= 0)
1952	  {
1953	    noteKeyboardEvent(keyCode, EventKeyUp, modifierState);
1954	    //accentMap= 0;
1955	  }
1956      }
1957      break;
1958
1959    case NSScrollWheel:
1960      {
1961	int keyCode, modifiers;
1962	keyCode= ([event deltaY] >= 0.0) ? 30 : 31;
1963	modifierState= qz2sqModifiers([event modifierFlags]);
1964	modifiers= modifierState ^ CtrlKeyBit;
1965	noteKeyboardEvent(keyCode, EventKeyDown, modifiers);
1966	noteKeyboardEvent(keyCode, EventKeyChar, modifiers);
1967	noteKeyboardEvent(keyCode, EventKeyUp,   modifiers);
1968      }
1969      break;
1970
1971    case NSAppKitDefined:
1972      switch ([event subtype])
1973	{
1974	case NSApplicationActivatedEventType:
1975	  active= 1;
1976	  break;
1977
1978	case NSApplicationDeactivatedEventType:
1979	  active= 0;
1980	  break;
1981	  // case NSScreenChangedEventType: //xxx this means the window
1982	  // changed to a different physical screen, which is useless
1983	  // info (we'd far rather be informed that the current screen's
1984	  // depth has changed)
1985	}
1986      //debugf(("AppKitDefinedEvent subtype %d\n", [event subtype]));
1987      [super sendEvent: event];
1988      break;
1989
1990      // case NSFlagsChanged:
1991      // case NSApplicationDefined: break;
1992      // case NSPeriodic: break;
1993      // case NSCursorUpdate: break;
1994
1995    default: // almost always NSSystemDefined
1996      //debugf(("Event type %d subtype %d\n", [event type], [event subtype]));
1997      [super sendEvent: event];
1998    }
1999}
2000
2001
2002@end // Squeak
2003
2004
2005
2006@implementation SqueakWindow
2007
2008- (BOOL) isOpaque		{ return YES; }
2009- (BOOL) canBecomeKeyWindow	{ return YES; }
2010
2011static NSImage *tryLoadingIcon(char *dir)
2012{
2013  char buf[MAXPATHLEN];
2014  sprintf(buf, "%s/SqueakVM.icns", dir);
2015  return [[NSImage alloc]
2016	   initWithContentsOfFile:
2017	     [NSString stringWithCString: buf]];
2018}
2019
2020- (void) setIcon
2021{
2022  icon= 0;
2023  if ((   icon= tryLoadingIcon("."))
2024      || (icon= tryLoadingIcon("/usr/local/lib/squeak"))
2025      || (icon= tryLoadingIcon(resourcePath)))
2026    [NSApp setApplicationIconImage: icon];
2027}
2028
2029#if 0
2030
2031- (NSImage *) dockImage
2032{
2033  NSBitmapImageRep *rep= [NSBitmapImageRep alloc];
2034  if ([rep initWithFocusedViewRect: topRect])
2035    {
2036      NSImage *image= [[NSImage alloc] init];
2037      [image addRepresentation: rep];
2038      if (icon)
2039	{
2040	  [image lockFocus];
2041	  [icon drawInRect: NSMakeRect(0, 0, [image size].width, [image size].height)
2042		fromRect:   NSMakeRect(0, 0, [icon size].width, [icon size].height)
2043		operation:  NSCompositeSourceOver
2044		fraction:   1.0];
2045	  [image unlockFocus];
2046	}
2047      return image;
2048    }
2049  return nil;
2050}
2051
2052- (void) miniaturize: (id)sender
2053{
2054  NSImage *image= [self dockImage];
2055  if (image)
2056    [self setMiniwindowImage: image];
2057  [image release];
2058  [super miniaturize: sender];
2059}
2060
2061#endif
2062
2063
2064- (void) performMiniaturize: (id)sender
2065{
2066  if (!glActive)
2067    [super performMiniaturize: sender];
2068}
2069
2070
2071@end // SqueakWindow
2072
2073
2074
2075@implementation SqueakView
2076
2077- (BOOL) isOpaque		{ return YES; }
2078- (BOOL) isFlipped		{ return YES; }
2079- (BOOL) acceptsFirstResponder	{ return YES; }
2080- (BOOL) becomeFirstResponder	{ return YES; }
2081- (BOOL) resignFirstResponder	{ return NO; }
2082
2083#if 0
2084- (void) renewGState
2085{
2086  printf("\nRENEW GSTATE\n\n");
2087  [super renewGState];
2088}
2089#endif
2090
2091static NSRange inputMark;
2092static NSRange inputSelection;
2093static int     inputCharCode;
2094
2095- (id) initWithFrame: (NSRect)frame
2096{
2097  id result= [super initWithFrame: frame];
2098  if (self == result)
2099    [self registerForDraggedTypes:
2100	    [NSArray arrayWithObjects:
2101		       NSFilenamesPboardType, nil]];
2102  inputCharCode=  -1;
2103  inputMark=	  NSMakeRange(NSNotFound, 0);
2104  inputSelection= NSMakeRange(0, 0);
2105  return result;
2106}
2107
2108
2109- (void) setFrame: (NSRect)rect
2110{
2111  lock(display);
2112  [super setFrame: rect];
2113  if ([self inLiveResize])
2114    {
2115#    if (RESIZE_IN_TITLE)
2116      display_winSetName(shortImageName);
2117#    endif
2118    }
2119  else
2120    if ([self qdPort])
2121      updatePix();
2122  unlock(display);
2123}
2124
2125
2126- (void) drawRect: (NSRect)rect		// view already has focus
2127{
2128#if 0
2129  printf("drawRect:\n");
2130#endif
2131  if ([self inLiveResize])
2132    {
2133      [[NSColor whiteColor] set];
2134      NSRectFill(rect);
2135      drawImage([[NSGraphicsContext currentContext] graphicsPort], 0);
2136    }
2137  else
2138    {
2139      if (!pixBase)
2140	{
2141#	 if 0
2142	  printf("drawRect: calling updatePix\n");
2143#	 endif
2144	  assert([self qdPort]);
2145	  updatePix();
2146	}
2147      fullDisplayUpdate();
2148    }
2149}
2150
2151- (void) viewWillStartLiveResize
2152{
2153  captureImage(1);
2154  [win setShowsResizeIndicator: YES];
2155
2156#if (RESIZE_IN_TITLE)
2157  showExtent= 1;
2158  display_winSetName(shortImageName);
2159#endif
2160  pixWidth= 0;
2161  pixHeight= 0;
2162}
2163
2164- (void) viewDidEndLiveResize
2165{
2166  releaseImage(1);
2167  [win setShowsResizeIndicator: NO];
2168#if (RESIZE_IN_TITLE)
2169  showExtent= 0;
2170  display_winSetName(shortImageName);
2171#endif
2172  updatePix();
2173  fullDisplayUpdate(); // gets rid of the resize icon if window didn't resize
2174}
2175
2176
2177- (int) draggingEntered: (id<NSDraggingInfo>)info
2178{
2179  if ((dragCount == 0) // cannot drag again until previous drag completes
2180      && ([info draggingSourceOperationMask] & NSDragOperationCopy))
2181    {
2182      int count= [[[info draggingPasteboard]
2183		    propertyListForType: NSFilenamesPboardType] count];
2184      noteMousePoint([info draggingLocation]);
2185      noteDragEvent(DragEnter, dragCount= count);
2186      return NSDragOperationCopy;
2187    }
2188  return NSDragOperationNone;
2189}
2190
2191- (int) draggingUpdated: (id<NSDraggingInfo>)info
2192{
2193  noteMousePoint([info draggingLocation]);
2194  noteDragEvent(DragMove, dragCount);
2195  return NSDragOperationCopy;
2196}
2197
2198- (void) draggingExited: (id<NSDraggingInfo>)info
2199{
2200  noteMousePoint([info draggingLocation]);
2201  noteDragEvent(DragLeave, dragCount);
2202  dragCount= 0;
2203}
2204
2205- (BOOL) performDragOperation: (id<NSDraggingInfo>)info
2206{
2207  NSPasteboard *pboard= [info draggingPasteboard];
2208  noteMousePoint([info draggingLocation]);
2209  if ([[pboard types] containsObject: NSFilenamesPboardType])
2210    {
2211      NSArray *files= [pboard propertyListForType: NSFilenamesPboardType];
2212      int i;
2213      if (uxDropFileCount)
2214	{
2215	  assert(uxDropFileNames);
2216	  for (i= 0;  i < uxDropFileCount;  ++i)
2217	    free(uxDropFileNames[i]);
2218	  free(uxDropFileNames);
2219	  uxDropFileNames= 0;
2220	}
2221      if ((  (!(uxDropFileCount= [files count])))
2222	  || (!(uxDropFileNames= (char **)malloc(uxDropFileCount * sizeof(char *)))))
2223	{
2224	  uxDropFileCount= 0;
2225	  return NO;
2226	}
2227      for (i= 0;  i < uxDropFileCount;  ++i)
2228	uxDropFileNames[i]= strdup([[files objectAtIndex: i] cString]);
2229    }
2230  noteDragEvent(DragDrop, uxDropFileCount);
2231  dragCount= 0;
2232
2233  return YES;	// under some duress, I might add (see sqUxDragDrop.c)
2234}
2235
2236
2237enum { KeyMapSize= 32 };
2238
2239typedef struct
2240{
2241  int keyCode;
2242  int keyChar;
2243} KeyMapping;
2244
2245static KeyMapping keyMap[KeyMapSize];
2246
2247static int keyMapSize=	   0;
2248static int inputCharCode= -1;
2249
2250static int addToKeyMap(int keyCode, int keyChar)
2251{
2252  if (keyMapSize > KeyMapSize) { fprintf(stderr, "keymap overflow\n");  return -1; }
2253  keyMap[keyMapSize++]= (KeyMapping){ keyCode, keyChar };
2254  return keyChar;
2255}
2256
2257static int indexInKeyMap(int keyCode)
2258{
2259  int i;
2260  for (i= 0;  i < keyMapSize;  ++i)
2261    if (keyMap[i].keyCode == keyCode)
2262      return i;
2263  return -1;
2264}
2265
2266static int findInKeyMap(int keyCode)
2267{
2268  int idx= indexInKeyMap(keyCode);
2269  return (idx >= 0) ? keyMap[idx].keyChar : -1;
2270}
2271
2272static int removeFromKeyMap(int keyCode)
2273{
2274  int idx= indexInKeyMap(keyCode);
2275  int keyChar= -1;
2276  if (idx < 0) { fprintf(stderr, "keymap underflow\n");  return -1; }
2277  keyChar= keyMap[idx].keyChar;
2278  for (; idx < keyMapSize - 1;  ++idx)
2279    keyMap[idx]= keyMap[idx + 1];
2280  --keyMapSize;
2281  return keyChar;
2282}
2283
2284
2285// the following (to @end) must be installed in the first responder
2286
2287- (int) composeKeyDown: (NSEvent *)event
2288{
2289  int keyCode= [event keyCode];
2290  inputCharCode= -1;
2291
2292  if (modifierState & CommandKeyBit)
2293    inputCharCode= qz2sqKey(event);
2294  else
2295    {
2296      if ([event isARepeat])
2297	return findInKeyMap(keyCode);
2298      else
2299	{
2300	  [self interpretKeyEvents: [NSArray arrayWithObject: event]];
2301	  if (inputCharCode < 0)
2302	    inputCharCode= qz2sqKey(event);
2303	}
2304    }
2305
2306  if (inputCharCode >= 0)
2307    addToKeyMap(keyCode, inputCharCode);
2308
2309  return inputCharCode;
2310}
2311
2312- (int) composeKeyUp: (NSEvent *)event
2313{
2314  return removeFromKeyMap([event keyCode]);
2315}
2316
2317- (void) insertText: text
2318{
2319  inputMark= NSMakeRange(NSNotFound, 0);
2320  inputSelection= NSMakeRange(0, 0);
2321  if ([text length])
2322    {
2323      UInt8 buf[4];
2324      CFIndex nUsed;
2325      if (CFStringGetBytes((CFStringRef)text, CFRangeMake(0, CFStringGetLength((CFStringRef)text)),
2326			   (CFStringEncoding)sqTextEncoding, 0, FALSE,
2327			   buf, sizeof(buf), &nUsed))
2328	inputCharCode= buf[0];
2329    }
2330}
2331
2332// ParagraphEditor's map looks like this:
2333//
2334//   0	noop cursorHome noop noop cursorEnd noop noop noop
2335//   8	backspace noop noop cursorPageUp cursorPageDown crWithIndent noop noop
2336//  16	noop noop noop noop noop noop noop noop
2337//  24	noop noop noop offerMenuFromEsc cursorLeft cursorRight cursorUp cursorDown
2338// 127  forwardDelete
2339
2340- (void) doCommandBySelector: (SEL)aSelector
2341{
2342  // why doesn't @selector() reduce to a constant??
2343# define encode(c, s)  if (aSelector == @selector(s)) inputCharCode= c
2344  // my (subjective) approximation of usage frequency...
2345       encode(  8, deleteBackward:);
2346  else encode( 13, insertNewline:);
2347  else encode(  9, insertTab:);
2348  else encode( 28, moveLeft:);
2349  else encode( 29, moveRight:);
2350  else encode( 30, moveUp:);
2351  else encode( 31, moveDown:);
2352  else encode( 11, pageUp:);
2353  else encode( 12, pageDown:);
2354  else encode(  1, moveToBeginningOfDocument:);
2355  else encode(  4, moveToEndOfDocument:);
2356  else encode(127, deleteForward:);
2357  else encode( 27, _cancelKey:);
2358  else
2359    printf("doCommandBySelector: %s\n", sel_getName(aSelector));
2360# undef encode
2361}
2362
2363- (void) setMarkedText: (id)aString selectedRange: (NSRange)selRange
2364{
2365  inputMark= NSMakeRange(0, 1);
2366  inputSelection= NSMakeRange(NSNotFound, 0);
2367}
2368
2369- (void)		 unmarkText						{ inputMark= NSMakeRange(NSNotFound, 0); }
2370- (BOOL)		 hasMarkedText						{ return inputMark.location != NSNotFound; }
2371- (long)		 conversationIdentifier					{ return (long)self; }
2372- (NSAttributedString *) attributedSubstringFromRange: (NSRange)theRange	{ return nil; }
2373- (NSRange)		 markedRange						{ return inputMark; }
2374- (NSRange)		 selectedRange						{ return inputSelection; }
2375- (NSRect)		 firstRectForCharacterRange: (NSRange)theRange		{ return NSMakeRect(0,0, 0,0); }
2376- (unsigned int)	 characterIndexForPoint: (NSPoint)thePoint		{ return 0; }
2377- (NSArray *)		 validAttributesForMarkedText				{ return nil; }
2378
2379@end // SqueakView
2380
2381
2382
2383///
2384/// Dialogues for sqUnixMain
2385///
2386
2387
2388@interface ProgressBar : NSPanel
2389{
2390  NSText		*message;
2391  NSProgressIndicator	*indicator;
2392  int			 value;
2393  NSModalSession	 session;
2394}
2395+(ProgressBar *) openWithTitle: (NSString *) title message: (NSString *) message;
2396-(void) displayProgressFrom: (int) min to: (int) max during: (void (*)(ProgressBar *)) thunk;
2397-(id)   value: (int) value;
2398-(id)   setMinValue: (int) value;
2399-(id)   setMaxValue: (int) value;
2400-(void) close;
2401@end
2402
2403@implementation ProgressBar
2404
2405-(id) initWithTitle: (NSString *) titleString message: (NSString *) messageString
2406{
2407  NSSize messageSize;
2408  NSProgressIndicator *ind;
2409  NSText *text;
2410  int inset, y, w;
2411
2412  message= 0;
2413  indicator= 0;
2414  value= 0;
2415  messageSize= (nil == messageString)
2416    ? NSMakeSize(0,0)
2417    : [messageString sizeWithAttributes: nil];
2418  inset= 10;
2419  y= inset;
2420  w= max(100, messageSize.width + 50);
2421  ind= [[NSProgressIndicator alloc]
2422	 initWithFrame: NSMakeRect(inset, y, w, NSProgressIndicatorPreferredThickness)];
2423  [ind setIndeterminate: NO];
2424  y += NSProgressIndicatorPreferredThickness + inset;
2425  text= [[NSText alloc] initWithFrame: NSMakeRect(inset, y, w, messageSize.height)];
2426  [text setString: messageString];
2427  [text setEditable: NO];
2428  y += messageSize.height + inset;
2429  if ((self= [super initWithContentRect: NSMakeRect(0, 0, w + inset * 2, y)
2430		    styleMask:           ((nil == titleString)
2431					  ? NSBorderlessWindowMask
2432					  : NSTitledWindowMask)
2433		    backing:             NSBackingStoreBuffered
2434		    defer:               NO]))
2435    {
2436      [[self contentView] addSubview: (indicator= ind)];
2437      [[self contentView] addSubview: (message= text)];
2438      if (nil != titleString)
2439	[self setTitle: titleString];
2440      session= [NSApp beginModalSessionForWindow: self];
2441    }
2442  return self;
2443}
2444
2445+(ProgressBar *) openWithTitle: (NSString *) titleString
2446		       message: (NSString *) messageString
2447{
2448  ProgressBar *bar= [[ProgressBar alloc] initWithTitle: titleString message: messageString];
2449  [bar center];
2450  [bar makeKeyAndOrderFront: nil];
2451  return bar;
2452}
2453
2454-(id) setMinValue: (int) min
2455{
2456  [indicator setMinValue: (double)min];
2457  return self;
2458}
2459
2460-(id) setMaxValue: (int) max
2461{
2462  [indicator setMaxValue: (double)max];
2463  return self;
2464}
2465
2466-(id) value: (int) newValue
2467{
2468  if (newValue != value)
2469    {
2470      value= newValue;
2471      [indicator setDoubleValue: (double) value];
2472      [indicator displayIfNeeded];
2473      [NSApp runModalSession: session];
2474    }
2475  return self;
2476}
2477
2478-(void) dealloc
2479{
2480  if (message) [message release];
2481  if (indicator) [indicator release];
2482  [super dealloc];
2483}
2484
2485-(void) close
2486{
2487  [NSApp endModalSession: session];
2488  [super close];
2489  [self release];
2490}
2491
2492-(void) displayProgressFrom: (int) min to: (int) max during: (void (*)(ProgressBar *)) thunk
2493{
2494  [indicator setMinValue: (double)min];
2495  [indicator setMaxValue: (double)max];
2496  thunk(self);
2497}
2498
2499@end // ProgressBar
2500
2501
2502static int fileCopy(char *src, char *dst)
2503{
2504  int in, out, r= -1;
2505  struct stat st;
2506  if (stat(src, &st)) return (errno= ENOENT);
2507  if ((in=  open(src, O_RDONLY)) < 0) return (errno= ENOENT);
2508  if ((out= open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0755)) >= 0)
2509    {
2510      char *buf;
2511      int  done;
2512      ProgressBar *bar= [ProgressBar openWithTitle: @"Writing..."
2513				     message: [NSString stringWithCString: dst]];
2514      [bar setMaxValue: (double)st.st_size];
2515      buf= (char *)alloca(st.st_blksize);
2516      done= 0;
2517      while ((r= read(in, buf, st.st_blksize)) > 0)
2518	if (r == write(out, buf, r))
2519	  {
2520	    done += r;
2521	    [bar value: done];
2522	  }
2523	else
2524	  {
2525	    r= -1;
2526	    break;
2527	  }
2528      [bar close];
2529      close(out);
2530    }
2531  close(in);
2532  return (r == 0) ? 0 : errno;
2533}
2534
2535
2536static void copyFile(const char *filename, char *ext, char *source)
2537{
2538  char dest[MAXPATHLEN], *ptr= 0;
2539  strncpy(dest, filename, sizeof(dest));
2540  if ((ptr= strrchr(dest, '.')))
2541    {
2542      strcpy(ptr, ext);
2543      if (fileCopy(source, dest))
2544	{
2545	  perror("FileCopy");
2546	  NSRunCriticalAlertPanel(@"Oops...",
2547				  @"I encountered an error while copying the image/changes files.  The system told me `%s'.  Sorry."
2548				  @"Quit",
2549				  nil,
2550				  nil,
2551				  [NSString stringWithCString: strerror(errno)]);
2552	  exit(1);
2553	}
2554    }
2555}
2556
2557
2558static void display_winImageNotFound(void)	{}
2559
2560
2561static int winCopyOrOpen(void)
2562{
2563  switch (NSRunAlertPanel(@"Create a new image?",
2564			  @"You have started Squeak without specifying an image file.  Would you like to create a new image or open an image that you saved earlier?",
2565			  @"Open",
2566			  @"Cancel",
2567			  @"New"))
2568    {
2569    case NSAlertDefaultReturn:	return 0;	// open
2570    case NSAlertOtherReturn:	return 1;	// new
2571    default:					// cancel or error
2572      exit(0);
2573    }
2574  return 0;
2575}
2576
2577
2578static int winImageCopy(char *buf, int len, char *image, char *changes)
2579{
2580  NSSavePanel *panel= [NSSavePanel savePanel];
2581  NSString    *home=  [NSString stringWithCString: getenv("HOME")];
2582  int          reply;
2583  //xxx release the string
2584
2585  [panel setTitle: @"Where should I save the new image file?"];
2586  [panel setRequiredFileType: @"image"];
2587  [panel setFloatingPanel: YES];
2588  [panel setOneShot: YES];
2589  [panel setReleasedWhenClosed: YES]; //xxx does the previous imply this???
2590//[panel setContentSize: NSMakeSize(400, 350)];
2591  [panel center];
2592
2593  reply= [panel runModalForDirectory: home file: @"squeak.image"];
2594//[home release];
2595
2596  if (NSFileHandlingPanelOKButton == reply)
2597    {
2598      const char *path= [[panel filename]  cString];
2599      copyFile(path, ".image",   image);
2600      copyFile(path, ".changes", changes);
2601      strncpy(buf, path, len);
2602      return 1;
2603    }
2604  return 0;
2605}
2606
2607
2608static int winImageOpen(char *buf, int len)
2609{
2610  NSOpenPanel *panel= [NSOpenPanel openPanel];
2611
2612  [panel setTitle: @"Which image file should I open?"];
2613  [panel setFloatingPanel: YES];
2614  [panel setOneShot: YES];
2615  [panel setReleasedWhenClosed: YES]; //xxx does the previous imply this???
2616//[panel setContentSize: NSMakeSize(400, 350)];
2617  [panel center];
2618
2619  if (NSOKButton == [panel runModalForTypes: [NSArray arrayWithObject: @"image"]])
2620    {
2621      NSArray *files= [panel filenames];
2622      if (1 == [files count])
2623	{
2624	  strncpy(buf, [[files objectAtIndex: 0] cString], len);
2625	  return 1;
2626	}
2627    }
2628  return 0;
2629}
2630
2631
2632static int display_winImageFind(char *buf, int len)
2633{
2634  if (documentName)
2635    {
2636      strncpy(buf, documentName, len);
2637      free(documentName);
2638      documentName= 0;
2639      return 1;
2640    }
2641  else
2642    {
2643      char image[MAXPATHLEN], changes[MAXPATHLEN];
2644      strlcat(strncpy(image, resourcePath, sizeof(image)),
2645	      "squeak.image",
2646	      sizeof(image));
2647      strlcat(strncpy(changes, resourcePath, sizeof(changes)),
2648	      "squeak.changes",
2649	      sizeof(changes));
2650      return ((  (0 == access(image,   R_OK)))
2651	      && (0 == access(changes, R_OK))
2652	      && winCopyOrOpen())
2653	? winImageCopy(buf, len, image, changes)
2654	: winImageOpen(buf, len);
2655    }
2656  return 0;
2657}
2658
2659
2660static sqInt display_primitivePluginBrowserReady(void)		{ return primitiveFail(); }
2661static sqInt display_primitivePluginRequestURLStream(void)	{ return primitiveFail(); }
2662static sqInt display_primitivePluginRequestURL(void)		{ return primitiveFail(); }
2663static sqInt display_primitivePluginPostURL(void)		{ return primitiveFail(); }
2664static sqInt display_primitivePluginRequestFileHandle(void)	{ return primitiveFail(); }
2665static sqInt display_primitivePluginDestroyRequest(void)	{ return primitiveFail(); }
2666static sqInt display_primitivePluginRequestState(void)		{ return primitiveFail(); }
2667
2668
2669///
2670/// OpenGL stuff
2671///
2672
2673
2674#include <OpenGL/OpenGL.h>
2675
2676#include "B3DAcceleratorPlugin.h"
2677#include "sqOpenGLRenderer.h"
2678#include "sqUnixQuartzGL.h"
2679
2680#define renderView(R)		(assert(R), (NSOpenGLView    *)((R)->drawable))
2681#define renderContext(R)	(assert(R), (NSOpenGLContext *)((R)->context))
2682
2683static glRenderer *renderers[MAX_RENDERER];
2684
2685static sqInt display_ioGLinitialise(void)
2686{
2687  int i;
2688  for (i= 0;  i < MAX_RENDERER;  ++i)
2689    renderers[i]= 0;
2690  glActive= 0;
2691  return 1;
2692}
2693
2694static void addRenderer(glRenderer *r)
2695{
2696  int i;
2697  for (i= 0;  i < MAX_RENDERER;  ++i)
2698    if (!renderers[i])
2699      {
2700	renderers[i]= r;
2701	++glActive;
2702	return;
2703      }
2704  assert(!"this cannot happen");
2705}
2706
2707static void removeRenderer(glRenderer *r)
2708{
2709  int i;
2710  for (i= 0;  i < MAX_RENDERER;  ++i)
2711    if (renderers[i] == r)
2712      {
2713	renderers[i]= 0;
2714	--glActive;
2715	return;
2716      }
2717  assert(!"this cannot happen");
2718}
2719
2720// fix dumb inverted coordinates after window geometry change
2721
2722static void reframeRenderer(glRenderer *r)
2723{
2724  NSRect frame= NSMakeRect(r->bufferRect[0], r->bufferRect[1],
2725			   r->bufferRect[2], r->bufferRect[3]);
2726  frame.origin.y= [topView bounds].size.height - frame.size.height - frame.origin.y;
2727  [renderView(r) removeFromSuperview];
2728  [renderView(r) setFrame: frame];
2729  [topView addSubview: renderView(r)];
2730}
2731
2732static void reframeRenderers(void)
2733{
2734  int i;
2735  for (i= 0;  i < MAX_RENDERER;  ++i)
2736    if (renderers[i])
2737      reframeRenderer(renderers[i]);
2738}
2739
2740#if 0
2741
2742static void updateRenderer(glRenderer *r)
2743{
2744  [[renderView(r) openGLContext] makeCurrentContext];
2745}
2746
2747static void updateRenderers(void)
2748{
2749  int i;
2750  for (i= 0;  i < MAX_RENDERER;  ++i)
2751    if (renderers[i])
2752      updateRenderer(renderers[i]);
2753}
2754
2755#endif
2756
2757
2758static sqInt display_ioGLcreateRenderer(glRenderer *r, sqInt x, sqInt y, sqInt w, sqInt h, sqInt flags)
2759{
2760  long swapInterval;
2761  NSOpenGLView *drawable;
2762  NSOpenGLPixelFormatAttribute attrs[]=
2763    {
2764      NSOpenGLPFANoRecovery,
2765      NSOpenGLPFAWindow,
2766      NSOpenGLPFAAccelerated,
2767      NSOpenGLPFADoubleBuffer,
2768      //NSOpenGLPFAColorSize,	16, //24
2769      NSOpenGLPFAAlphaSize,	 8, //8
2770      NSOpenGLPFADepthSize,	24, //16
2771      NSOpenGLPFAStencilSize,	((flags & B3D_STENCIL_BUFFER) ? 8 : 0),
2772      NSOpenGLPFAAccumSize, 0,
2773      0
2774    };
2775  NSOpenGLPixelFormat *fmt= [[NSOpenGLPixelFormat alloc] initWithAttributes: attrs];
2776  if (!fmt)
2777    {
2778      fprintf(stderr, "ioGLcreateRenderer: illegal pixel format\n");
2779      return 0;
2780    }
2781
2782  if (verboseLevel >= 3)
2783    printFormatInfo(fmt);
2784
2785  drawable= [[NSOpenGLView alloc]
2786	      initWithFrame: NSMakeRect(x, [topView bounds].size.height - h - y, w, h)
2787	      pixelFormat:   fmt];
2788  [fmt release];
2789  if (!drawable)
2790    {
2791      fprintf(stderr, "ioGLcreateRenderer: could not create view\n");
2792      return 0;
2793    }
2794  r->drawable= drawable;
2795  r->context=  [drawable openGLContext];
2796  addRenderer(r);
2797
2798  swapInterval= 0;
2799
2800  [renderContext(r) setValues: &swapInterval forParameter: NSOpenGLCPSwapInterval];
2801  [topView addSubview: drawable];
2802
2803  return 1;
2804
2805  (void)glErrString;	// declared static in sqOpenGLRenderer.h, but never used
2806}
2807
2808
2809static sqInt display_ioGLmakeCurrentRenderer(glRenderer *r)
2810{
2811  if (r)
2812    {
2813      assert(r->context);
2814      [renderContext(r) makeCurrentContext];
2815    }
2816  else
2817    [NSOpenGLContext clearCurrentContext];
2818
2819  return 1;
2820}
2821
2822
2823static void display_ioGLdestroyRenderer(glRenderer *r)
2824{
2825  [NSOpenGLContext clearCurrentContext];
2826  assert(r->drawable);
2827  [renderView(r) removeFromSuperview];
2828  [renderView(r) release];
2829  removeRenderer(r);
2830}
2831
2832
2833static void display_ioGLswapBuffers(glRenderer *r)
2834{
2835  assert(r->context);
2836  [renderContext(r) flushBuffer];
2837}
2838
2839
2840static void display_ioGLsetBufferRect(glRenderer *r, sqInt x, sqInt y, sqInt w, sqInt h)
2841{
2842  NSRect frame= NSMakeRect(x, y, w, h);
2843  fprintf(stderr, "ioGLsetBufferRect(%p, %d, %d, %d, %d)\n", r->context, x, y, w, h);
2844  assert(r->context);
2845  frame.origin.y= [topView bounds].size.height - frame.size.height - frame.origin.y;
2846  fprintf(stderr, "view setFrame: %d %d %d %d\n",
2847	  (int)frame.origin.x, (int)frame.origin.y, (int)frame.size.width, (int)frame.size.height);
2848  [renderView(r) setFrame: frame];
2849}
2850
2851//
2852// Host Window support
2853//
2854
2855#if (SqDisplayVersionMajor >= 1 && SqDisplayVersionMinor >= 2)
2856static int display_hostWindowClose(int index)                                               { return 0; }
2857static int display_hostWindowCreate(int w, int h, int x, int y,
2858  char *list, int attributeListLength)                                                      { return 0; }
2859static int display_hostWindowShowDisplay(unsigned *dispBitsIndex, int width, int height, int depth,
2860  int affectedL, int affectedR, int affectedT, int affectedB, int windowIndex)              { return 0; }
2861static int display_hostWindowGetSize(int windowIndex)                                       { return -1; }
2862static int display_hostWindowSetSize(int windowIndex, int w, int h)                         { return -1; }
2863static int display_hostWindowGetPosition(int windowIndex)                                   { return -1; }
2864static int display_hostWindowSetPosition(int windowIndex, int x, int y)                     { return -1; }
2865static int display_hostWindowSetTitle(int windowIndex, char *newTitle, int sizeOfTitle)     { return -1; }
2866static int display_hostWindowCloseAll(void)                                                 { return 0; }
2867#endif
2868
2869
2870SqDisplayDefine(Quartz);
2871
2872
2873#include "SqModule.h"
2874
2875static void *display_makeInterface(void)
2876{
2877  return &display_Quartz_itf;
2878}
2879
2880SqModuleDefine(display, Quartz);
2881