1/* -*- c-basic-offset:2; tab-width:2; indent-tabs-mode:nil -*- */
2
3#import <UIKit/UIKit.h>
4
5#include "ui_window.h"
6#include "ui_scrollbar.h"
7
8#include <pobl/bl_privilege.h>
9#include <pobl/bl_unistd.h> /* bl_getuid/bl_getgid */
10#include <pobl/bl_mem.h>
11#include <pobl/bl_str.h>  /* bl_compare_str */
12#include "../ui_screen_manager.h"
13#include "../ui_event_source.h"
14#include "../ui_selection_encoding.h"
15
16@interface Application : UIApplication
17@end
18
19@interface AppDelegate : NSObject <UIApplicationDelegate> {
20  UIWindow *window;
21}
22
23@property (nonatomic, retain) IBOutlet UIWindow *window;
24@end
25
26@interface TextPosition : UITextPosition {
27  int position;
28}
29@property (nonatomic) int position;
30@end
31
32@interface TextRange : UITextRange {
33  TextPosition *start;
34  TextPosition *end;
35}
36@property (nonatomic, readonly) UITextPosition *start;
37@property (nonatomic, readonly) UITextPosition *end;
38@end
39
40@interface MLTermView : UIView<UITextInput> {
41  ui_window_t *uiwindow;
42  CGContextRef ctx;
43  CGLayerRef layer;
44  int forceExpose; /* 2 = visual bell */
45
46  BOOL ignoreKeyDown;
47  NSString *markedText;
48  TextRange *selectedTextRange;
49  TextRange *markedTextRange;
50
51  int cand_x;
52  int cand_y;
53
54  NSArray *cmds;
55}
56
57@property (readwrite, copy) UITextRange *selectedTextRange;
58@property (nonatomic, readonly) UITextRange *markedTextRange;
59
60- (void)drawString:(ui_font_t *)font
61                  :(ui_color_t *)fg_color
62                  :(int)x
63                  :(int)y
64                  :(u_char *)str
65                  :(size_t)len;
66- (void)drawString16:(ui_font_t *)font
67                    :(ui_color_t *)fg_color
68                    :(int)x
69                    :(int)y
70                    :(XChar2b *)str
71                    :(size_t)len;
72- (void)fillWith:(ui_color_t *)color:(int)x:(int)y:(u_int)width:(u_int)height;
73- (void)drawRectFrame:(ui_color_t *)color:(int)x1:(int)y1:(int)x2:(int)y2;
74- (void)copyArea:(Pixmap)src
75                :(int)src_x
76                :(int)src_y
77                :(u_int)width
78                :(u_int)height
79                :(int)dst_x
80                :(int)dst_y;
81#if 0
82- (void)scroll:(int)src_x:(int)src_y:(u_int)width:(u_int)height:(int)dst_x:(int)dst_y;
83#endif
84- (void)setClip:(int)x:(int)y:(u_int)width:(u_int)height;
85- (void)unsetClip;
86- (void)update:(int)flag;
87- (void)bgColorChanged;
88@end
89
90/* --- static variables --- */
91
92static ui_window_t *uiwindow_for_mlterm_view;
93static int keyboard_margin;
94
95static u_int key_code;
96static u_int key_mod;
97
98/* --- static functions --- */
99
100#define set_fill_color(color)                                            \
101  CGContextSetRGBFillColor(ctx, (((color)->pixel >> 16) & 0xff) / 255.0, \
102                           (((color)->pixel >> 8) & 0xff) / 255.0,       \
103                           ((color)->pixel & 0xff) / 255.0, 1.0);
104
105#if 0
106#define IS_OPAQUE                                          \
107  ((uiwindow->bg_color.pixel & 0xff000000) == 0xff000000 || \
108   ui_window_has_wall_picture(uiwindow))
109#else
110#define IS_OPAQUE 1
111#endif
112
113#ifdef DEBUG
114int main_loop_final(void);
115#endif
116
117static void exit_program(void) {
118#ifdef DEBUG
119  main_loop_final();
120  bl_alloca_garbage_collect();
121  bl_mem_free_all();
122  bl_dl_close_all();
123#endif
124}
125
126static void monitor_pty(void) {
127#if 0
128  /* normal user (Don't call before UIApplicationMain()) */
129  bl_priv_change_euid(bl_getuid());
130  bl_priv_change_egid(bl_getgid());
131#endif
132
133  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
134      ui_event_source_process();
135    });
136}
137
138/* Undocumented */
139bool CGFontGetGlyphsForUnichars(CGFontRef, unichar[], CGGlyph[], size_t);
140
141static void drawUnistr(CGContextRef ctx, ui_font_t *font, unichar *str,
142                       u_int len, int x, int y) {
143  CGGlyph glyphs_buf[len];
144  CGGlyph *glyphs;
145
146#ifdef USE_OT_LAYOUT
147  if (font->use_ot_layout /* && font->otf */) {
148    glyphs = str;
149  } else
150#endif
151  {
152    glyphs = memset(glyphs_buf, 0, sizeof(CGGlyph) * len);
153    CGFontGetGlyphsForUnichars(font->xfont->cg_font, str, glyphs_buf, len);
154
155    for (; len > 0 && glyphs[len - 1] == 0; len--) ;
156  }
157
158  CGContextSetFont(ctx, font->xfont->cg_font);
159
160  CGAffineTransform t;
161
162  if (font->xfont->is_italic) {
163    CGFloat f = -tanf(-12.0 * acosf(0) / 90);
164    t = CGAffineTransformMake(1.0, 0.0, f, 1.0, -y * f, 0.0);
165  } else {
166    t = CGAffineTransformIdentity;
167  }
168
169  u_int width = font->width;
170
171  u_int fontsize = font->xfont->size;
172  switch (font->size_attr) {
173    case DOUBLE_WIDTH:
174      width /= 2;
175      x = (x + 1) / 2;
176      t = CGAffineTransformScale(t, 2.0, 1.0);
177      break;
178
179    case DOUBLE_HEIGHT_TOP:
180    case DOUBLE_HEIGHT_BOTTOM:
181      fontsize *= 2;
182      break;
183  }
184
185  CGContextSetTextMatrix(ctx, t);
186
187  CGPoint points[len];
188
189  if (font->is_proportional) {
190    int advances[len];
191    if (!CGFontGetGlyphAdvances(font->xfont->cg_font, glyphs, len, advances)) {
192      return;
193    }
194
195    int units = CGFontGetUnitsPerEm(font->xfont->cg_font);
196    int cur_x = x;
197    u_int count;
198    for (count = 0; count < len; count++) {
199      points[count] = CGPointMake(cur_x, y);
200
201      if (advances[count] > 0) {
202        cur_x += (advances[count] * fontsize / units);
203      }
204    }
205  } else {
206    u_int count;
207
208    x += font->x_off;
209
210    for (count = 0; count < len; count++) {
211      points[count] = CGPointMake((x + width * count), y);
212    }
213  }
214
215  CGContextSetFontSize(ctx, fontsize);
216
217  CGContextShowGlyphsAtPositions(ctx, glyphs, points, len);
218
219  if (font->double_draw_gap) {
220    int gap = font->double_draw_gap;
221
222    font->double_draw_gap = 0;
223    drawUnistr(ctx, font, str, len, x + font->double_draw_gap, y);
224    font->double_draw_gap = gap;
225  }
226}
227
228static void update_ime_text(ui_window_t *uiwindow, const char *preedit_text,
229                            const char *cur_preedit_text) {
230  (*uiwindow->preedit)(uiwindow, preedit_text, cur_preedit_text);
231}
232
233static void show_dialog(const char *msg) {
234  if (![NSThread isMainThread]) {
235    return;
236  }
237
238  NSString *ns_msg = [NSString stringWithCString:msg encoding:NSUTF8StringEncoding];
239
240  UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Alert"
241                                                  message:ns_msg
242                                                 delegate:nil
243                                        cancelButtonTitle:nil
244                                        otherButtonTitles:@"OK", nil];
245  [alert autorelease];
246  [alert show]; /* XXX This doesn't stop. */
247}
248
249static ui_window_t *search_focused_window(ui_window_t *win) {
250  u_int count;
251  ui_window_t *focused;
252
253  /*
254   * *parent* - *child*
255   *            ^^^^^^^ => Hit this window instead of the parent window.
256   *          - child
257   *          - child
258   * (**: is_focused == 1)
259   */
260  for (count = 0; count < win->num_children; count++) {
261    if ((focused = search_focused_window(win->children[count]))) {
262      return focused;
263    }
264  }
265
266  if (win->is_focused) {
267    return win;
268  }
269
270  return NULL;
271}
272
273/* --- class --- */
274
275int cocoa_dialog_alert(const char *msg);
276
277@implementation Application
278
279- (void)sendEvent:(UIEvent *)event {
280  [super sendEvent:event];
281
282  if ([event respondsToSelector:@selector(_gsEvent)]) {
283    u_int32_t *buf = [event performSelector:@selector(_gsEvent)];
284
285    if (buf && buf[2] == 10 /* Event type */) {
286      u_int num;
287      ui_display_t **disps = ui_get_opened_displays(&num);
288      ui_window_t *win = search_focused_window(disps[0]->roots[0]);
289
290      if (win) {
291        MLTermView *view = win->my_window;
292
293        if (![view hasText]) {
294          key_mod = buf[12];
295          key_code = (buf[15] >> 16) & 0xffff;
296          [self sendAction:@selector(keyEvent) to:view from:nil forEvent:nil];
297        }
298      }
299    }
300  }
301}
302
303@end
304
305@implementation AppDelegate
306
307@synthesize window;
308
309#pragma mark -
310#pragma mark Application lifecycle
311
312- (BOOL)application:(UIApplication *)application
313                   didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
314  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
315  [self.window makeKeyAndVisible];
316
317  [[NSNotificationCenter defaultCenter] addObserver:self
318                                           selector:@selector(keyboardDidShow:)
319                                               name:UIKeyboardDidShowNotification
320                                             object:nil];
321  [[NSNotificationCenter defaultCenter] addObserver:self
322                                           selector:@selector(keyboardDidHide:)
323                                               name:UIKeyboardDidHideNotification
324                                             object:nil];
325
326  CGRect r = [self.window screen].applicationFrame;
327	MLTermView *view = [[MLTermView alloc] initWithFrame:CGRectMake(0, 0,
328                                                                  r.size.width, r.size.height)];
329	[self.window addSubview:view];
330	[self.window makeKeyAndVisible];
331
332	return YES;
333}
334
335- (void)keyboardDidShow:(NSNotification *)note {
336  CGRect r = [[[note userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
337  keyboard_margin = r.size.height;
338  self.window.frame = self.window.frame; /* call observeValueForKeyPath */
339}
340
341- (void)keyboardDidHide:(NSNotification *)note {
342  keyboard_margin = 0;
343  self.window.frame = self.window.frame;
344}
345
346- (void)applicationWillResignActive:(UIApplication *)application {
347}
348
349
350- (void)applicationDidEnterBackground:(UIApplication *)application {
351}
352
353
354- (void)applicationWillEnterForeground:(UIApplication *)application {
355}
356
357
358- (void)applicationDidBecomeActive:(UIApplication *)application {
359}
360
361
362- (void)applicationWillTerminate:(UIApplication *)application {
363}
364
365#pragma mark -
366#pragma mark Memory management
367
368- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
369}
370
371- (void)dealloc {
372  [[NSNotificationCenter defaultCenter] removeObserver:self];
373  exit_program();
374  [super dealloc];
375}
376
377@end
378
379@implementation TextPosition
380@synthesize position;
381@end
382
383@implementation TextRange
384@synthesize start;
385@synthesize end;
386
387- (id)init {
388  [super init];
389
390  start = [TextPosition alloc];
391  end = [TextPosition alloc];
392
393  return self;
394}
395
396- (void)dealloc {
397  [super dealloc];
398
399  [start release];
400  [end release];
401}
402@end
403
404@implementation MLTermView
405
406@synthesize selectedTextRange;
407@synthesize markedTextRange;
408@synthesize tokenizer;
409@synthesize inputDelegate;
410@synthesize endOfDocument;
411@synthesize beginningOfDocument;
412@synthesize markedTextStyle;
413
414- (id)initWithFrame:(CGRect)frame {
415  if (uiwindow_for_mlterm_view) {
416    uiwindow = uiwindow_for_mlterm_view;
417  } else {
418    char args[] = "mlclient";
419    ui_mlclient(args, NULL);
420
421    ui_screen_t **screens;
422    u_int num = ui_get_all_screens(&screens);
423    if (num == 0) {
424      cocoa_dialog_alert("Failed to open screen");
425      exit(1);
426    }
427
428    uiwindow = &screens[num - 1]->window;
429  }
430
431  uiwindow->my_window = (UIView *)self;
432  forceExpose = 1;
433
434  self.clearsContextBeforeDrawing = NO;
435
436  [super initWithFrame:frame];
437
438  ignoreKeyDown = FALSE;
439  markedText = nil;
440  markedTextRange = [UITextRange alloc];
441  selectedTextRange = [UITextRange alloc];
442
443  UILongPressGestureRecognizer *longpress =
444    [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
445  longpress.minimumPressDuration = 2.0;
446  [longpress autorelease];
447  [self addGestureRecognizer:longpress];
448
449  if (uiwindow_for_mlterm_view) {
450    uiwindow_for_mlterm_view = NULL;
451  }
452
453  static int app_init;
454  if (!app_init) {
455    app_init = 1;
456    monitor_pty();
457  }
458
459  return self;
460}
461
462- (void)dealloc {
463  [[NSNotificationCenter defaultCenter] removeObserver:self];
464  [self.window removeObserver:self forKeyPath:@"frame"];
465
466  [markedTextRange release];
467  [selectedTextRange release];
468
469  if (layer) {
470    CGLayerRelease(layer);
471    layer = nil;
472  }
473
474  if (cmds) {
475    [cmds release];
476  }
477
478  [super dealloc];
479}
480
481- (BOOL)canPerformAction:(SEL)action withSender:(id)sender {
482  if (action == @selector(configMenu:) || action == @selector(pasteMenu:) ||
483      action == @selector(keyEvent:)) {
484    return YES;
485  } else {
486    return NO;
487  }
488}
489
490- (void)configMenu:(id)sender {
491  cocoa_dialog_alert("Configuration menu is not supported.");
492}
493
494- (void)pasteMenu:(id)sender {
495  /* if by any change */
496  if (((ui_screen_t *)uiwindow)->term) {
497    ui_screen_exec_cmd((ui_screen_t *)uiwindow, "paste");
498  }
499}
500
501- (void)setFrame:(CGRect)r {
502  if (layer) {
503    CGLayerRelease(layer);
504    layer = nil;
505  }
506
507  CGRect sr = [self.window screen].applicationFrame;
508
509  r.origin.x += sr.origin.x;
510  r.origin.y += sr.origin.y;
511
512#if 0
513  NSLog(@"setFrame %@ %f %f %f %f", self, r.origin.x, r.origin.y, r.size.width, r.size.height);
514#endif
515
516  [super setFrame:r];
517}
518
519- (void)windowResized {
520  if (!uiwindow->parent || !((ui_screen_t *)uiwindow)->term) {
521    /* It has been already removed from ui_layout or term has been detached. */
522    return;
523  }
524
525  CGRect sr = [self.window screen].applicationFrame;
526
527  uiwindow->parent->width = sr.size.width - uiwindow->parent->hmargin * 2;
528  uiwindow->parent->height = sr.size.height - uiwindow->parent->vmargin * 2 - keyboard_margin;
529
530  (*uiwindow->parent->window_resized)(uiwindow->parent);
531
532  u_int count;
533  for (count = 0; count < uiwindow->num_children; count++) {
534    ui_window_t *child = uiwindow->children[count];
535
536    if (child->my_window) {
537      [((UIView *)child->my_window) setFrame:CGRectMake(child->x, child->y,
538                                                        ACTUAL_WIDTH(child), ACTUAL_HEIGHT(child))];
539    }
540  }
541}
542
543- (void)observeValueForKeyPath:(NSString *)keyPath
544                      ofObject:(id)object
545                        change:(NSDictionary *)change
546                       context:(void *)context {
547  [self windowResized];
548}
549
550- (void)didMoveToWindow {
551  if ([self window] == nil) {
552    /* just before being deallocated */
553    return;
554  }
555
556#if 0
557  if (!uiwindow->parent->my_window) {
558    [[self window] orderOut:self];
559
560    if (uiwindow->event_mask & PointerMotionMask) {
561      [self window].acceptsMouseMovedEvents = YES;
562    }
563  }
564#endif
565
566  [self.window addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew
567               context:nil];
568
569  /* Change view size */
570
571  if (!uiwindow->parent->my_window) {
572    CGRect sr = [self.window screen].applicationFrame;
573
574    uiwindow->disp->width = sr.size.width;
575    uiwindow->disp->height = sr.size.height;
576
577#if 0
578    [[self window] useOptimizedDrawing:YES];
579#endif
580
581    uiwindow->parent->my_window = [self window];
582
583    if (!IS_OPAQUE) {
584      [self bgColorChanged];
585    }
586
587    /* XXX TODO: Support color change */
588    u_long pixel = uiwindow->fg_color.pixel; /* See window_exposed() in ui_layout.c */
589    self.window.backgroundColor = [UIColor colorWithRed:((pixel >> 16) & 0xff) / 255.0
590                                                  green:((pixel >> 8) & 0xff) / 255.0
591                                                   blue:(pixel & 0xff) / 255.0
592                                                  alpha:((pixel >> 24) & 0xff) / 255.0];
593  }
594
595  [self windowResized];
596  [self becomeFirstResponder];
597}
598
599- (void)drawRect:(CGRect)rect {
600  if (!uiwindow->parent || !((ui_screen_t *)uiwindow)->term) {
601    /* It has been already removed from ui_layout or term has been detached. */
602    return;
603  }
604
605  XExposeEvent ev;
606
607  CGContextRef screen_ctx = UIGraphicsGetCurrentContext();
608  CGContextSaveGState(screen_ctx);
609  CGContextTranslateCTM(screen_ctx, 0.0, self.bounds.size.height);
610  CGContextScaleCTM(screen_ctx, 1.0, -1.0);
611  CGContextSetBlendMode(screen_ctx, kCGBlendModeCopy);
612
613#if 0
614  CGAffineTransform t = CGContextGetCTM(ctx);
615  bl_debug_printf("%f %f %f %f %f %f\n", t.a, t.b, t.c, t.d, t.tx, t.ty);
616#endif
617
618  if (!layer) {
619    layer = CGLayerCreateWithContext(screen_ctx, self.bounds.size, NULL);
620    ctx = CGLayerGetContext(layer);
621    CGContextSetBlendMode(ctx, kCGBlendModeCopy);
622
623    if (uiwindow->update_window_flag == 0) {
624      uiwindow->update_window_flag = 3; /* UPDATE_SCREEN|UPDATE_CURSOR (ui_screen.c) */
625    }
626    forceExpose = 1;
627  }
628
629  CGPoint p = CGPointMake(0, 0);
630
631  if (forceExpose & 2) {
632    /* Visual bell */
633    [self fillWith:&uiwindow->fg_color
634                  :uiwindow->hmargin
635                  :uiwindow->vmargin
636                  :uiwindow->width
637                  :uiwindow->height];
638    CGContextFlush(ctx);
639    CGContextDrawLayerAtPoint(screen_ctx, p, layer);
640
641    [[NSRunLoop currentRunLoop]
642        runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
643
644    forceExpose &= ~2;
645    uiwindow->update_window_flag = 0;
646  }
647
648  ev.type = UI_EXPOSE;
649  ev.x = rect.origin.x;
650  ev.width = rect.size.width;
651  ev.height = rect.size.height;
652  ev.y = ACTUAL_HEIGHT(uiwindow) - rect.origin.y - ev.height;
653  ev.force_expose = forceExpose;
654
655  ui_window_receive_event(uiwindow, (XEvent *)&ev);
656
657  forceExpose = 0;
658  uiwindow->update_window_flag = 0;
659
660  CGContextDrawLayerAtPoint(screen_ctx, p, layer);
661
662  CGContextRestoreGState(screen_ctx);
663}
664
665- (BOOL)isOpaque {
666  return IS_OPAQUE ? YES : NO;
667}
668
669- (BOOL)wantsDefaultClipping {
670  return IS_OPAQUE ? YES : NO;
671}
672
673static ui_window_t *get_current_window(ui_window_t *win) {
674  u_int count;
675
676  if (win->inputtable > 0) {
677    return win;
678  }
679
680  for (count = 0; count < win->num_children; count++) {
681    ui_window_t *hit;
682
683    if ((hit = get_current_window(win->children[count]))) {
684      return hit;
685    }
686  }
687
688  return NULL;
689}
690
691- (BOOL)acceptsFirstResponder {
692  return YES;
693}
694
695- (BOOL)becomeFirstResponder {
696  if ([super becomeFirstResponder] != YES) {
697    return NO;
698  }
699
700  [self.window bringSubviewToFront:self];
701
702  XEvent ev;
703  ev.type = UI_KEY_FOCUS_IN;
704
705  ui_window_receive_event(uiwindow, &ev);
706
707  return YES;
708}
709
710- (BOOL)canBecomeFirstResponder {
711  return YES;
712}
713
714- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
715  UITouch *touch = [touches anyObject];
716  CGPoint loc = [touch locationInView:self];
717
718  XButtonEvent bev;
719  bev.type = UI_BUTTON_PRESS;
720  bev.time = touch.timestamp * 1000;
721  bev.x = loc.x;
722  bev.y = loc.y;
723  bev.state = 0;
724  bev.button = 1;
725  bev.click_count = touch.tapCount;
726
727  ui_window_receive_event(uiwindow, (XEvent *)&bev);
728
729  if (!uiwindow->is_focused || keyboard_margin == 0) {
730    [self becomeFirstResponder];
731  }
732}
733
734- (void)longPress:(id)sender {
735  UIMenuController *menuctl = [UIMenuController sharedMenuController];
736  CGPoint loc = [sender locationOfTouch:0 inView:self];
737  [menuctl setTargetRect:CGRectMake(loc.x, loc.y, 0, 0) inView:self];
738  menuctl.arrowDirection = UIMenuControllerArrowDown;
739
740  NSMutableArray *items = [NSMutableArray array];
741  UIMenuItem *item;
742  item = [[[UIMenuItem alloc] initWithTitle:@"Paste" action:@selector(pasteMenu:)] autorelease];
743  [items addObject:item];
744  item = [[[UIMenuItem alloc] initWithTitle:@"Config" action:@selector(configMenu:)] autorelease];
745  [items addObject:item];
746  menuctl.menuItems = items;
747  [menuctl setMenuVisible:NO animated:NO];
748  [menuctl setMenuVisible:YES animated:YES];
749}
750
751- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
752  UITouch *touch = [touches anyObject];
753  CGPoint loc = [touch locationInView:self];
754
755  XButtonEvent bev;
756  bev.type = UI_BUTTON_RELEASE;
757  bev.time = touch.timestamp * 1000;
758  bev.x = loc.x;
759  bev.y = loc.y;
760  bev.state = 0;
761  bev.button = 1;
762  bev.click_count = touch.tapCount;
763
764  ui_window_receive_event(uiwindow, (XEvent *)&bev);
765}
766
767- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
768  UITouch *touch = [touches anyObject];
769  CGPoint loc = [touch locationInView:self];
770
771  XMotionEvent mev;
772  mev.type = UI_BUTTON_MOTION;
773  mev.time = touch.timestamp * 1000;
774  mev.x = loc.x;
775  mev.y = loc.y;
776  mev.state = Button1Mask;
777
778  ui_window_receive_event(uiwindow, (XEvent *)&mev);
779}
780
781#if 0
782- (void)scrollWheel:(NSEvent *)event {
783  NSPoint loc = [event locationInWindow];
784  XButtonEvent bevPress;
785  XButtonEvent bevRelease;
786
787  bevPress.type = UI_BUTTON_PRESS;
788  bevRelease.type = UI_BUTTON_RELEASE;
789
790  bevPress.time = event.timestamp * 1000;
791  bevRelease.time = (event.timestamp * 1000) + 1;
792
793  bevPress.x = loc.x - self.frame.origin.x;
794  bevRelease.x = loc.x - self.frame.origin.x;
795
796  bevPress.y =
797      ACTUAL_HEIGHT(uiwindow->parent) - loc.y - /* self.frame.origin.y - */ 1;
798  bevRelease.y =
799      ACTUAL_HEIGHT(uiwindow->parent) - loc.y - /* self.frame.origin.y - */ 1;
800
801  bevPress.state = event.modifierFlags &
802                   (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask);
803  bevRelease.state = event.modifierFlags &
804                     (NSShiftKeyMask | NSControlKeyMask | NSAlternateKeyMask);
805
806  bevPress.click_count = 1;
807
808  if (event.deltaY > 1) {
809    bevPress.button = 4;
810    bevRelease.button = 4;
811  }
812
813  if (event.deltaY < -1) {
814    bevPress.button = 5;
815    bevRelease.button = 5;
816  }
817
818  if (event.deltaY < -1 || event.deltaY > 1) {
819    ui_window_receive_event(uiwindow, (XEvent *)&bevPress);
820    ui_window_receive_event(uiwindow, (XEvent *)&bevRelease);
821  }
822}
823#endif
824
825- (void)keyEvent {
826  XKeyEvent kev;
827
828#if 0
829  NSLog(@"Key event: mod %x keycode %x", key_mod, key_code);
830#endif
831
832  if (0xf700 <= key_code && key_code <= 0xf8ff) {
833    /* do nothing (Function keys) */
834  } else if (key_code == 0x1b) {
835    /* do nothing */
836  } else if ((key_mod & NSControlKeyMask) || (key_mod & NSCommandKeyMask)) {
837    if ('a' <= key_code && key_code <= 'z') {
838      key_code -= 0x60;
839    } else if (0x40 <= key_code && key_code < 0x60) {
840      key_code -= 0x40;
841    } else {
842      return;
843    }
844  } else {
845    return;
846  }
847
848  kev.type = UI_KEY_PRESS;
849  kev.state = key_mod;
850  kev.keysym = key_code;
851  kev.utf8 = NULL;
852  ui_window_receive_event(uiwindow, (XEvent *)&kev);
853}
854
855#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 /* iOS 7.0 or later */
856- (NSArray *)keyCommands {
857  if (!cmds) {
858    UIKeyModifierFlags flags[] = {
859      0,
860      UIKeyModifierShift,
861      UIKeyModifierShift | UIKeyModifierControl,
862      UIKeyModifierShift | UIKeyModifierAlternate,
863      UIKeyModifierShift | UIKeyModifierCommand,
864      UIKeyModifierShift | UIKeyModifierControl | UIKeyModifierAlternate,
865      UIKeyModifierShift | UIKeyModifierControl | UIKeyModifierAlternate | UIKeyModifierCommand,
866      UIKeyModifierShift | UIKeyModifierControl | UIKeyModifierCommand,
867      UIKeyModifierShift | UIKeyModifierAlternate | UIKeyModifierCommand,
868      UIKeyModifierShift | UIKeyModifierCommand,
869      UIKeyModifierControl,
870      UIKeyModifierControl | UIKeyModifierAlternate,
871      UIKeyModifierControl | UIKeyModifierAlternate | UIKeyModifierCommand,
872      UIKeyModifierControl | UIKeyModifierCommand,
873      UIKeyModifierAlternate,
874      UIKeyModifierAlternate | UIKeyModifierCommand,
875      UIKeyModifierCommand };
876    NSMutableArray *mutable = [[NSMutableArray alloc] init];
877    u_char c[] = "\x60";
878    size_t count;
879
880    for (; c[0] < 0x80; c[0]++) {
881      [mutable addObject:[UIKeyCommand keyCommandWithInput:[NSString stringWithUTF8String:c]
882                                             modifierFlags:UIKeyModifierControl
883                                                    action:@selector(keyEvent:)]];
884    }
885
886    for (count = 0; count < sizeof(flags) / sizeof(flags[0]); count++) {
887      [mutable addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputUpArrow
888                                             modifierFlags:flags[count]
889                                                    action:@selector(keyEvent:)]];
890      [mutable addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputDownArrow
891                                             modifierFlags:flags[count]
892                                                    action:@selector(keyEvent:)]];
893      [mutable addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputLeftArrow
894                                             modifierFlags:flags[count]
895                                                    action:@selector(keyEvent:)]];
896      [mutable addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputRightArrow
897                                             modifierFlags:flags[count]
898                                                    action:@selector(keyEvent:)]];
899      [mutable addObject:[UIKeyCommand keyCommandWithInput:UIKeyInputEscape
900                                             modifierFlags:flags[count]
901                                                    action:@selector(keyEvent:)]];
902
903      [mutable addObject:[UIKeyCommand keyCommandWithInput:@"\x01"
904                                             modifierFlags:flags[count]
905                                                    action:@selector(keyEvent:)]];
906      [mutable addObject:[UIKeyCommand keyCommandWithInput:@"\x04"
907                                             modifierFlags:flags[count]
908                                                    action:@selector(keyEvent:)]];
909      [mutable addObject:[UIKeyCommand keyCommandWithInput:@"\x0b"
910                                             modifierFlags:flags[count]
911                                                    action:@selector(keyEvent:)]];
912      [mutable addObject:[UIKeyCommand keyCommandWithInput:@"\x0c"
913                                             modifierFlags:flags[count]
914                                                    action:@selector(keyEvent:)]];
915      [mutable addObject:[UIKeyCommand keyCommandWithInput:@"\x10"
916                                             modifierFlags:flags[count]
917                                                    action:@selector(keyEvent:)]];
918     }
919
920    cmds = mutable;
921  }
922
923  return cmds;
924}
925
926- (void)keyEvent:(UIKeyCommand *)keyCommand {
927  key_mod = keyCommand.modifierFlags;
928
929#if 0
930  NSLog(@"KeyCmd: %x %x\n", key_mod, [keyCommand.input characterAtIndex:0]);
931  NSLog(keyCommand.input);
932#endif
933
934  if ([keyCommand.input compare:UIKeyInputUpArrow] == NSOrderedSame) {
935    key_code = NSUpArrowFunctionKey;
936  } else if ([keyCommand.input compare:UIKeyInputDownArrow] == NSOrderedSame) {
937    key_code = NSDownArrowFunctionKey;
938  } else if ([keyCommand.input compare:UIKeyInputLeftArrow] == NSOrderedSame) {
939    key_code = NSLeftArrowFunctionKey;
940  } else if ([keyCommand.input compare:UIKeyInputRightArrow] == NSOrderedSame) {
941    key_code = NSRightArrowFunctionKey;
942  } else if ([keyCommand.input compare:UIKeyInputEscape] == NSOrderedSame) {
943    key_code = 0x1b;
944  } else {
945    key_code = [keyCommand.input characterAtIndex:0];
946
947    switch (key_code) {
948    case 0x1:
949      key_code = NSHomeFunctionKey;
950      break;
951
952    case 0x4:
953      key_code = NSEndFunctionKey;
954      break;
955
956    case 0xb:
957      key_code = NSPageUpFunctionKey;
958      break;
959
960    case 0xc:
961      key_code = NSPageDownFunctionKey;
962      break;
963
964    case 0x10:
965      /* iOS sends 0x10 for all function keys. It's impossible to distinguish them. */
966      key_code = NSF1FunctionKey;
967      break;
968
969    default:
970      break;
971    }
972  }
973
974  [self keyEvent];
975}
976#endif
977
978- (UITextRange *)characterRangeAtPoint:(CGPoint)point {
979  return nil;
980}
981
982- (UITextPosition *)closestPositionToPoint:(CGPoint)point {
983  return nil;
984}
985
986- (UITextPosition *)closestPositionToPoint:(CGPoint)point withinRange:(UITextRange *)range {
987  return nil;
988}
989
990- (CGRect)firstRectForRange:(UITextRange *)range {
991#if 0
992  int x = cand_x + uiwindow->x + uiwindow->hmargin;
993  int y = ACTUAL_HEIGHT(uiwindow->parent) - (cand_y + uiwindow->y + uiwindow->vmargin);
994
995  if (vt_term_get_vertical_mode(((ui_screen_t*)uiwindow)->term)) {
996    /*
997     * XXX Adjust candidate window position.
998     *
999     * +-+-+------
1000     * | |1|ABCDE
1001     *
1002     * <-->^
1003     *  25 x
1004     */
1005    x += 25;
1006  }
1007
1008  CGRect r = CGRectMake(x, y, ui_col_width((ui_screen_t *)uiwindow),
1009                        ui_line_height((ui_screen_t *)uiwindow));
1010#endif
1011
1012  return CGRectMake(0, 0, 1, 1);
1013}
1014
1015- (CGRect)caretRectForPosition:(UITextPosition *)position {
1016  return CGRectMake(0, 0, 1, 1);
1017}
1018
1019- (void)setBaseWritingDirection:(UITextWritingDirection)writingDirection
1020                       forRange:(UITextRange *)range {
1021}
1022
1023- (UITextWritingDirection)baseWritingDirectionForPosition:(UITextPosition *)position
1024                                              inDirection:(UITextStorageDirection)direction {
1025  return UITextWritingDirectionLeftToRight;
1026}
1027
1028- (UITextRange *)characterRangeByExtendingPosition:(UITextPosition *)position
1029                                       inDirection:(UITextLayoutDirection)direction {
1030  return nil;
1031}
1032
1033- (UITextPosition *)positionWithinRange:(UITextRange *)range
1034                    farthestInDirection:(UITextLayoutDirection)direction {
1035  return nil;
1036}
1037
1038- (NSInteger)offsetFromPosition:(UITextPosition *)from
1039                     toPosition:(UITextPosition *)toPosition {
1040  return ((TextPosition*)toPosition).position - ((TextPosition*)from).position;
1041}
1042
1043- (NSComparisonResult)comparePosition:(UITextPosition *)position
1044                           toPosition:(UITextPosition *)other {
1045  int p = ((TextPosition *)position).position;
1046  int o = ((TextPosition *)other).position;
1047
1048  if (p < o) {
1049    return NSOrderedAscending;
1050  } else if (p > 0) {
1051    return NSOrderedDescending;
1052  } else {
1053    return NSOrderedSame;
1054  }
1055}
1056
1057- (UITextPosition *)positionFromPosition:(UITextPosition *)position
1058                                  offset:(NSInteger)offset {
1059  TextPosition *pos = [[TextPosition alloc] autorelease];
1060  pos.position = ((TextPosition *)position).position + offset;
1061
1062  return pos;
1063}
1064
1065- (UITextPosition *)positionFromPosition:(UITextPosition *)position
1066                             inDirection:(UITextLayoutDirection)direction
1067                                  offset:(NSInteger)offset {
1068  return nil;
1069}
1070
1071- (UITextRange *)textRangeFromPosition:(UITextPosition *)fromPosition
1072                            toPosition:(UITextPosition *)toPosition {
1073  TextRange *range = [[TextRange alloc] autorelease];
1074  ((TextPosition *)range.start).position = ((TextPosition *)fromPosition).position;
1075  ((TextPosition *)range.end).position = ((TextPosition *)toPosition).position;
1076
1077  return range;
1078}
1079
1080- (void)showMarkedText:(id)string
1081         selectedRange:(NSRange)selectedRange {
1082  if ([string length] > 0) {
1083    char *p;
1084
1085    if (!(p = alloca(strlen([string UTF8String]) + 10))) {
1086      return;
1087    }
1088    *p = '\0';
1089
1090    if (selectedRange.location > 0) {
1091      strcpy(p, [[string substringWithRange:
1092                           NSMakeRange(0, selectedRange.location)] UTF8String]);
1093    }
1094
1095    if (selectedRange.length > 0) {
1096      sprintf(p + strlen(p), "\x1b[7m%s\x1b[27m",
1097              [[string substringWithRange:selectedRange] UTF8String]);
1098    }
1099
1100    if (selectedRange.location + selectedRange.length < [string length]) {
1101      strcat(p, [[string substringWithRange:
1102                             NSMakeRange(
1103                                 selectedRange.location + selectedRange.length,
1104                                 [string length] - selectedRange.location -
1105                                     selectedRange.length)] UTF8String]);
1106    }
1107
1108    if (!markedText) {
1109      if (!uiwindow->xim_listener ||
1110          !(*uiwindow->xim_listener->get_spot)(uiwindow->xim_listener->self, &cand_x, &cand_y)) {
1111        cand_x = cand_y = 0;
1112      }
1113    }
1114
1115    update_ime_text(uiwindow, p, markedText ? markedText.UTF8String : NULL);
1116  } else if (markedText) {
1117    update_ime_text(uiwindow, "", markedText.UTF8String);
1118  }
1119}
1120
1121- (void)setMarkedText:(id)string
1122        selectedRange:(NSRange)selectedRange {
1123  if ([string isKindOfClass:[NSAttributedString class]]) {
1124    string = [string string];
1125  }
1126
1127  ((TextPosition *)selectedTextRange.start).position = 0;
1128  ((TextPosition *)selectedTextRange.end).position = selectedRange.location +
1129                                                     selectedRange.length;
1130
1131  [self showMarkedText:string selectedRange:selectedRange];
1132
1133  if (markedText) {
1134    [markedText release];
1135    markedText = nil;
1136  }
1137
1138  if ([string length] > 0) {
1139    markedText = [string copy];
1140    ((TextPosition *)markedTextRange.end).position = [string length];
1141  } else {
1142    ((TextPosition *)markedTextRange.end).position = 0;
1143  }
1144  ((TextPosition *)markedTextRange.start).position = 0;
1145}
1146
1147- (void)unmarkText {
1148  if (markedText) {
1149    update_ime_text(uiwindow, "", NULL);
1150    [self insertText:markedText];
1151    [markedText release];
1152    markedText = nil;
1153  }
1154}
1155
1156- (void)replaceRange:(UITextRange *)range withText:(NSString *)text {
1157  if (!markedText) {
1158    NSRange range = NSMakeRange(0, [text length]);
1159    [self setMarkedText:text selectedRange:range];
1160
1161    return;
1162  }
1163
1164  int start = ((TextPosition *)range.start).position;
1165  int length = ((TextPosition *)range.end).position - start;
1166  NSMutableString *nsstr = [NSMutableString stringWithString:markedText];
1167  [nsstr replaceCharactersInRange:NSMakeRange(start, length) withString:text];
1168  [markedText release];
1169  markedText = [NSString stringWithString:nsstr];
1170  [nsstr release];
1171  ((TextPosition *)selectedTextRange.start).position = start;
1172  ((TextPosition *)selectedTextRange.start).position = start + [text length];
1173
1174  [self showMarkedText:markedText selectedRange:NSMakeRange(start, [text length])];
1175}
1176
1177- (NSString *)textInRange:(UITextRange *)range {
1178  return markedText;
1179}
1180
1181- (BOOL)hasText {
1182  if (markedText) {
1183    return YES;
1184  } else {
1185    return NO;
1186  }
1187}
1188
1189- (void)insertText:(NSString *)string {
1190  if ([string length] > 0) {
1191    unichar c = [string characterAtIndex:0];
1192
1193    if (0xf700 <= c && c <= 0xf8ff) {
1194      /* Function keys (See keyEvent or keyEvent:) */
1195      return;
1196    }
1197
1198    XKeyEvent kev;
1199    kev.type = UI_KEY_PRESS;
1200    kev.state = 0;
1201    kev.keysym = c;
1202    kev.utf8 = [string UTF8String];
1203
1204    ui_window_receive_event(uiwindow, (XEvent *)&kev);
1205
1206    ignoreKeyDown = TRUE;
1207  }
1208}
1209
1210- (void)deleteBackward {
1211    XKeyEvent kev;
1212
1213    kev.type = UI_KEY_PRESS;
1214    kev.state = 0;
1215    kev.keysym = 0x08;
1216    kev.utf8 = "\x08";
1217
1218    ui_window_receive_event(uiwindow, (XEvent *)&kev);
1219}
1220
1221- (void)drawString:(ui_font_t *)font
1222                  :(ui_color_t *)fg_color
1223                  :(int)x
1224                  :(int)y
1225                  :(u_char *)str
1226                  :(size_t)len {
1227  set_fill_color(fg_color);
1228
1229#if 0
1230  u_char *p = alloca(len + 1);
1231  memcpy(p, str, len);
1232  p[len] = '\0';
1233  bl_debug_printf("%d %d %s %x\n", x, y, p, p[len - 1]);
1234#endif
1235
1236#if 0
1237  CGContextSelectFont(ctx, "Menlo", 16, kCGEncodingMacRoman);
1238  CGContextShowTextAtPoint(ctx, x, ACTUAL_HEIGHT(uiwindow) - y - 1, str, len);
1239#else
1240  unichar ustr[len];
1241  int count;
1242  for (count = 0; count < len; count++) {
1243    ustr[count] = str[count];
1244  }
1245
1246  drawUnistr(ctx, font, ustr, len, x, ACTUAL_HEIGHT(uiwindow) - y - 1);
1247#endif
1248}
1249
1250- (void)drawString16:(ui_font_t *)font
1251                    :(ui_color_t *)fg_color
1252                    :(int)x
1253                    :(int)y
1254                    :(XChar2b *)str
1255                    :(size_t)len {
1256  set_fill_color(fg_color);
1257
1258  drawUnistr(ctx, font, (unichar *)str, len, x, ACTUAL_HEIGHT(uiwindow) - y - 1);
1259}
1260
1261- (void)fillWith:(ui_color_t *)color:(int)x:(int)y:(u_int)width:(u_int)height {
1262#if 0
1263  static int count = 0;
1264  color->pixel += (0x10 * (count++));
1265#endif
1266
1267  if (IS_OPAQUE) {
1268    set_fill_color(color);
1269
1270    CGRect rect =
1271        CGRectMake(x, ACTUAL_HEIGHT(uiwindow) - y - height, width, height);
1272    CGContextAddRect(ctx, rect);
1273    CGContextFillPath(ctx);
1274  } else {
1275    [[UIColor colorWithRed:((color->pixel >> 16) & 0xff) / 255.0
1276                     green:((color->pixel >> 8) & 0xff) / 255.0
1277                      blue:(color->pixel & 0xff) / 255.0
1278                     alpha:((color->pixel >> 24) & 0xff) / 255.0] set];
1279
1280#if 0
1281    NSRectFillUsingOperation(
1282        NSMakeRect(x, ACTUAL_HEIGHT(uiwindow) - y - height, width, height),
1283        NSCompositeCopy);
1284#endif
1285  }
1286}
1287
1288- (void)drawRectFrame:(ui_color_t *)color:(int)x1:(int)y1:(int)x2:(int)y2 {
1289  set_fill_color(color);
1290
1291  CGRect rect = CGRectMake(x1, ACTUAL_HEIGHT(uiwindow) - y2 - 1, 1, y2 - y1);
1292  CGContextAddRect(ctx, rect);
1293  rect = CGRectMake(x1, ACTUAL_HEIGHT(uiwindow) - y2 - 1, x2 - x1, 1);
1294  CGContextAddRect(ctx, rect);
1295  rect = CGRectMake(x2, ACTUAL_HEIGHT(uiwindow) - y2 - 1, 1, y2 - y1);
1296  CGContextAddRect(ctx, rect);
1297  rect = CGRectMake(x1, ACTUAL_HEIGHT(uiwindow) - y1 - 1, x2 - x1 + 1, 1);
1298  CGContextAddRect(ctx, rect);
1299  CGContextFillPath(ctx);
1300}
1301
1302- (void)copyArea:(Pixmap)src
1303                :(int)src_x
1304                :(int)src_y
1305                :(u_int)width
1306                :(u_int)height
1307                :(int)dst_x
1308                :(int)dst_y {
1309  CGImageRef clipped = CGImageCreateWithImageInRect(
1310      src, CGRectMake(src_x, src_y, width, height));
1311  CGContextDrawImage(
1312      ctx,
1313      CGRectMake(dst_x, ACTUAL_HEIGHT(uiwindow) - dst_y - height, width, height),
1314      clipped);
1315  CGImageRelease(clipped);
1316}
1317
1318#if 0
1319- (void)scroll:(int)src_x:(int)src_y:(u_int)width:(u_int)height:(int)dst_x:(int)dst_y {
1320  CGContextFlush(ctx);
1321
1322  /* Don't release this CGImage */
1323  CGRect src_r = CGRectMake(src_x, ACTUAL_HEIGHT(uiwindow) - src_y - height,
1324			width, height);
1325  UIBitmapImageRep *bir = [self bitmapImageRepForCachingDisplayInRect:src_r];
1326  [self cacheDisplayInRect:src_r toBitmapImageRep:bir];
1327  CGImageRef image = bir.CGImage;
1328
1329  CGRect dst_r = CGRectMake(dst_x, ACTUAL_HEIGHT(uiwindow) - dst_y - height,
1330				width, height);
1331  CGContextDrawImage(ctx, dst_r, image);
1332
1333  static int i;
1334  if (i == 0) {
1335    CFURLRef url = [UIURL fileURLWithPath:@"/Users/ken/kallist/log.png"];
1336    CGImageDestinationRef dest = CGImageDestinationCreateWithURL(
1337				   url, kUTTypePNG, 1, NULL);
1338    CGImageDestinationAddImage(dest, image, nil);
1339    CGImageDestinationFinalize(dest);
1340    CFRelease( dest);
1341    i++;
1342  }
1343  else if (i == 1) {
1344    UIImage *  nsimage = [[[UIImage alloc]initWithSize:src_r.size] autorelease];
1345    [nsimage addRepresentation:bir];
1346    [[nsimage TIFFRepresentation] writeToFile:@"/Users/ken/kallist/log.tiff"
1347                                  atomically:YES];
1348
1349    i++;
1350  }
1351}
1352#endif
1353
1354- (void)update:(int)flag {
1355  forceExpose |= flag;
1356
1357  if (IS_OPAQUE || forceExpose) {
1358    [self setNeedsDisplay];
1359  } else {
1360    int x;
1361    int y;
1362
1363    if (!uiwindow->xim_listener ||
1364        !(*uiwindow->xim_listener->get_spot)(uiwindow->xim_listener->self, &x, &y)) {
1365      x = y = 0;
1366    }
1367
1368    x += (uiwindow->hmargin);
1369    y += (uiwindow->vmargin);
1370
1371    [self setNeedsDisplayInRect:CGRectMake(x, ACTUAL_HEIGHT(uiwindow) - y, 1, 1)];
1372  }
1373}
1374
1375- (void)setClip:(int)x:(int)y:(u_int)width:(u_int)height {
1376  CGContextSaveGState(ctx);
1377
1378  y = ACTUAL_HEIGHT(uiwindow) - y;
1379
1380  CGContextBeginPath(ctx);
1381  CGContextMoveToPoint(ctx, x, y);
1382  CGContextAddLineToPoint(ctx, x + width, y);
1383  CGContextAddLineToPoint(ctx, x + width, y - height);
1384  CGContextAddLineToPoint(ctx, x, y - height);
1385  CGContextClosePath(ctx);
1386  CGContextClip(ctx);
1387}
1388
1389- (void)unsetClip {
1390  CGContextRestoreGState(ctx);
1391}
1392
1393- (void)bgColorChanged {
1394  if (IS_OPAQUE) {
1395    [[self window] setBackgroundColor:[UIColor whiteColor]];
1396    [[self window] setOpaque:YES];
1397  } else {
1398    [[self window] setBackgroundColor:[UIColor clearColor]];
1399    [[self window] setOpaque:NO];
1400  }
1401}
1402@end
1403
1404/* --- global functions --- */
1405
1406void view_alloc(ui_window_t *uiwindow) {
1407  uiwindow_for_mlterm_view = uiwindow;
1408
1409  MLTermView *view =
1410      [[MLTermView alloc] initWithFrame:CGRectMake(0, 0, 400, 400)];
1411  [((UIWindow *)uiwindow->parent->my_window) addSubview:view];
1412}
1413
1414void view_dealloc(UIView *view) {
1415  /* removeFromSuperview() can hide keyboard and change the frame of UIWindow. */
1416  [view.window removeObserver:view forKeyPath:@"frame"];
1417  [view removeFromSuperview];
1418  [view release];
1419}
1420
1421void view_update(MLTermView *view, int flag) { [view update:flag]; }
1422
1423void view_set_clip(MLTermView *view, int x, int y, u_int width, u_int height) {
1424  [view setClip:x:y:width:height];
1425}
1426
1427void view_unset_clip(MLTermView *view) { [view unsetClip]; }
1428
1429void view_draw_string(MLTermView *view, ui_font_t *font, ui_color_t *fg_color,
1430                      int x, int y, char *str, size_t len) {
1431  [view drawString:font:fg_color:x:y:str:len];
1432}
1433
1434void view_draw_string16(MLTermView *view, ui_font_t *font, ui_color_t *fg_color,
1435                        int x, int y, XChar2b *str, size_t len) {
1436  [view drawString16:font:fg_color:x:y:str:len];
1437}
1438
1439void view_fill_with(MLTermView *view, ui_color_t *color, int x, int y,
1440                    u_int width, u_int height) {
1441  [view fillWith:color:x:y:width:height];
1442}
1443
1444void view_draw_rect_frame(MLTermView *view, ui_color_t *color, int x1, int y1,
1445                          int x2, int y2) {
1446  [view drawRectFrame:color:x1:y1:x2:y2];
1447}
1448
1449void view_copy_area(MLTermView *view, Pixmap src, int src_x, int src_y,
1450                    u_int width, u_int height, int dst_x, int dst_y) {
1451  [view copyArea:src:src_x:src_y:width:height:dst_x:dst_y];
1452}
1453
1454void view_scroll(MLTermView *view, int src_x, int src_y, u_int width,
1455                 u_int height, int dst_x, int dst_y) {
1456#if 0
1457  [view scroll:src_x:src_y:width:height:dst_x:dst_y];
1458#endif
1459}
1460
1461void view_bg_color_changed(MLTermView *view) { [view bgColorChanged]; }
1462
1463void view_visual_bell(MLTermView *view) { [view update:2]; }
1464
1465void view_set_input_focus(UIView *view) {
1466  [view becomeFirstResponder];
1467}
1468
1469void view_set_rect(UIView *view, int x, int y, /* The origin is left-botom. */
1470                   u_int width, u_int height) {
1471  [view setFrame:CGRectMake(x,y,width,height)];
1472}
1473
1474void view_set_hidden(UIView *view, int flag) { [view setHidden:flag]; }
1475
1476void window_alloc(ui_window_t *root) {
1477  uiwindow_for_mlterm_view = root->children[1];
1478
1479  UINib *nib = [[UINib alloc] nibWithNibName:@"Main" bundle:nil];
1480  [nib instantiateWithOwner:nil options:nil];
1481  [nib release];
1482}
1483
1484void window_dealloc(UIWindow *window) { [window release]; }
1485
1486void window_resize(UIWindow *window, int width, int height) {}
1487
1488void window_move_resize(UIWindow *window, int x, int y, int width, int height) {}
1489
1490void window_accepts_mouse_moved_events(UIWindow *window, int accept) {
1491#if 0
1492  window.acceptsMouseMovedEvents = (accept ? YES : NO);
1493#endif
1494}
1495
1496void window_set_normal_hints(UIWindow *window, u_int width_inc,
1497                             u_int height_inc) {
1498#if 0
1499  [window setResizeIncrements:NSMakeSize(width_inc, height_inc)];
1500#endif
1501}
1502
1503void window_get_position(UIWindow *window, int *x, int *y) {
1504  *x = 0;
1505  *y = 0;
1506}
1507
1508void window_set_title(UIWindow *window, const char *title /* utf8 */) {
1509#if 0
1510  NSString *ns_title = [NSString stringWithCString:title encoding:NSUTF8StringEncoding];
1511  [window setTitle:ns_title];
1512#endif
1513}
1514
1515void app_urgent_bell(int on) {
1516#if 0
1517  if (on) {
1518    [[NSApplication sharedApplication] requestUserAttention:NSCriticalRequest];
1519  } else {
1520    [[NSApplication sharedApplication]
1521        cancelUserAttentionRequest:NSCriticalRequest];
1522  }
1523#endif
1524}
1525
1526void scroller_update(UIScrollView *scroller, float pos, float knob) {
1527#if 0
1528#if 1
1529  [scroller setFloatValue:pos knobProportion:knob]; /* Deprecated since 10.6 */
1530#else
1531  scroller.knobProportion = knob;
1532  scroller.doubleValue = pos;
1533#endif
1534#endif
1535}
1536
1537CGFontRef cocoa_create_font(const char *font_family) {
1538  NSString *ns_font_family =
1539      [NSString stringWithCString:font_family encoding:NSUTF8StringEncoding];
1540
1541  return CGFontCreateWithFontName(ns_font_family);
1542}
1543
1544#ifdef USE_OT_LAYOUT
1545char *cocoa_get_font_path(CGFontRef cg_font) {
1546#if 0
1547  CTFontDescriptorRef desc =
1548      CTFontDescriptorCreateWithNameAndSize(CGFontCopyFullName(cg_font), 14);
1549  CFURLRef url =
1550      (CFURLRef)CTFontDescriptorCopyAttribute(desc, kCTFontURLAttribute);
1551  const char *urlstr = [((NSString *)CFURLGetString(url))UTF8String];
1552  if (strncmp(urlstr, "file://localhost", 16) == 0) {
1553    urlstr += 16;
1554  }
1555
1556  char *path = strdup(urlstr);
1557
1558  CFRelease(url);
1559  CFRelease(desc);
1560
1561  return path;
1562#else
1563  return NULL;
1564#endif
1565}
1566#endif /* USE_OT_LAYOUT */
1567
1568void cocoa_release_font(CGFontRef cg_font) { CFRelease(cg_font); }
1569
1570u_int cocoa_font_get_advance(CGFontRef cg_font, u_int fontsize, int size_attr,
1571                             unichar *utf16, u_int len, CGGlyph glyph) {
1572  if (utf16) {
1573    CGFontGetGlyphsForUnichars(cg_font, &utf16, &glyph, 1);
1574  }
1575
1576  int advance;
1577  if (!CGFontGetGlyphAdvances(cg_font, &glyph, 1, &advance) || advance < 0) {
1578    return 0;
1579  }
1580
1581  if (size_attr >= DOUBLE_HEIGHT_TOP) {
1582    fontsize *= 2;
1583  }
1584
1585  return advance * fontsize / CGFontGetUnitsPerEm(cg_font);
1586}
1587
1588void cocoa_clipboard_own(MLTermView *view) {
1589#if 0
1590  UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1591  [pasteboard declareTypes:[NSArray arrayWithObject:NSPasteboardTypeString]
1592                     owner:view];
1593#endif
1594}
1595
1596void cocoa_clipboard_set(const u_char *utf8, size_t len) {
1597  UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1598  NSString *str = [[NSString alloc] initWithBytes:utf8
1599                                           length:len
1600                                         encoding:NSUTF8StringEncoding];
1601  pasteboard.string = str;
1602  [str release];
1603}
1604
1605const char *cocoa_clipboard_get(void) {
1606  UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
1607
1608  NSString *str = pasteboard.string;
1609  if (str != nil) {
1610    return [str UTF8String];
1611  }
1612
1613  return NULL;
1614}
1615
1616void cocoa_beep(void) {}
1617
1618CGImageRef cocoa_load_image(const char *path, u_int *width, u_int *height) {
1619  NSString *nspath =
1620      [NSString stringWithCString:path encoding:NSUTF8StringEncoding];
1621  UIImage *uiimg = [[UIImage alloc] initWithContentsOfFile:nspath];
1622  if (!uiimg) {
1623    return nil;
1624  }
1625
1626  CGImageRef cgimg = uiimg.CGImage;
1627
1628  CGImageRetain(cgimg);
1629
1630  *width = uiimg.size.width;
1631  *height = uiimg.size.height;
1632  [uiimg release];
1633
1634  return cgimg;
1635}
1636
1637const char *cocoa_get_bundle_path(void) {
1638  return [[[NSBundle mainBundle] bundlePath] UTF8String];
1639}
1640
1641char *cocoa_dialog_password(const char *msg) {
1642#if 0
1643  NSAlert *alert = create_dialog(msg, 1);
1644  if (alert == nil) {
1645    return NULL;
1646  }
1647
1648  NSTextField *text = [[MLSecureTextField alloc] initWithFrame:NSMakeRect(0, 0, 200, 20)];
1649  [text autorelease];
1650  [alert setAccessoryView:text];
1651
1652  if ([alert runModal] == NSAlertFirstButtonReturn) {
1653    return strdup([[text stringValue] UTF8String]);
1654  } else
1655#endif
1656  {
1657    return NULL;
1658  }
1659}
1660
1661int cocoa_dialog_okcancel(const char *msg) {
1662  return -1;
1663}
1664
1665int cocoa_dialog_alert(const char *msg) {
1666  show_dialog(msg);
1667
1668  return 1;
1669}
1670