~ubuntu-branches/ubuntu/quantal/terminal.app/quantal

« back to all changes in this revision

Viewing changes to .#TerminalView.m.1.5

  • Committer: Package Import Robot
  • Author(s): Yavor Doganov
  • Date: 2012-06-14 13:55:31 UTC
  • mfrom: (4.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20120614135531-xbagikvsm7nmcq25
Tags: 0.9.8-1
* New upstream release:
  + Fixes FTBFS with ld that defaults to --as-needed (Closes: #639040).
* TerminalParser_LinuxPrefs.m:
* TerminalView.m (-initWithFrame:):
* TerminalViewPrefs.m:
* ServicesPrefs.m: Revert all direct modifications.
* debian/control (Maintainer, Uploaders): Make the team maintainer, add
  myself.
  (Section): Change to `gnustep'.
  (Build-Depends): Require debhelper >= 8.  Remove version requirement
  for libgnustep-gui-dev.  Add dpkg-dev (>= 1.16.1~) and imagemagick.
  Drop gnustep-make.
  (Homepage): Point to the new location.
  (Vcs-Git, Vcs-Browser): New fields.
  (Conflicts, Replaces): Remove, no longer needed.
  (Depends): Add ${misc:Depends}.
  (Standards-Version): Claim compliance with 3.9.3 as of this release.
* debian/compat: Bump to 8.
* debian/rules: Get rid of gs_make.  Enable hardening, verbose builds,
  support for `noopt'.
  (d_app): New convenience variable.
  (build-stamp): Convert the app icon in XPM format.
  (clean): Delete the generated icon.
  (install): Replace the deprecated `dh_clean -k' with dh_prep.  Don't
  install lintian overrides.  Install the XPM icon.
  (binary-arch): Remove unnecessary arguments to dh_installman and
  dh_shlibdeps.  Move arch-independent Resources to /usr/share.
* debian/source/format: New file; set format to 3.0 (quilt).
* debian/overrides: Delete.
* debian/prerm: Don't ignore errors.
* debian/preinst:
* debian/watch: New file.
* debian/menu: Add icon.
* debian/Terminal.desktop: Make it valid.  Add Bulgarian translation.
* debian/Terminal.1: Fix errors from man.  Update homepage.
* debian/docs: Delete; duplicated by the manpage.
* debian/copyright: Switch to format 1.0, update copyright years, add
  many copyright holders.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
copyright 2002, 2003 Alexander Malmberg <alexander@malmberg.org>
 
3
forkpty replacement, 2005-2008 Riccardo Mottola <rmottola@users.sf.net>
 
4
 
 
5
This file is a part of Terminal.app. Terminal.app is free software; you
 
6
can redistribute it and/or modify it under the terms of the GNU General
 
7
Public License as published by the Free Software Foundation; version 2
 
8
of the License. See COPYING or main.m for more information.
 
9
*/
 
10
 
 
11
/*
 
12
TODO: Move pty and child process handling to another class. Make this a
 
13
stupid but fast character cell display view.
 
14
*/
 
15
 
 
16
/* define this if you need the forkpty replacement and it is not automatically
 
17
activated */
 
18
#undef USE_FORKPTY_REPLACEMENT
 
19
 
 
20
#define USE_FORKPTY_REPLACEMENT 1
 
21
 
 
22
/* check for solaris */
 
23
#if defined (__SVR4) && defined (__sun)
 
24
#define __SOLARIS__ 1
 
25
#define USE_FORKPTY_REPLACEMENT 1
 
26
#endif
 
27
 
 
28
#include <math.h>
 
29
#include <unistd.h>
 
30
 
 
31
#ifdef __NetBSD__
 
32
#  include <sys/types.h>
 
33
#  include <sys/ioctl.h>
 
34
#  include <termios.h>
 
35
#  include <pcap.h>
 
36
#define TCSETS TIOCSETA
 
37
#else
 
38
#ifdef freebsd
 
39
#  include <sys/types.h>
 
40
#  include <sys/ioctl.h>
 
41
#  include <termios.h>
 
42
#  include <libutil.h>
 
43
#  include <pcap.h>
 
44
#else
 
45
#  include <termio.h>
 
46
#endif
 
47
#endif
 
48
 
 
49
#include <sys/time.h>
 
50
#include <sys/types.h>
 
51
#include <unistd.h>
 
52
#include <fcntl.h>
 
53
#ifndef freebsd
 
54
#if !(defined (__NetBSD__)) && !(defined (__SOLARIS__))
 
55
#  include <pty.h>
 
56
#endif
 
57
#endif
 
58
 
 
59
#include <Foundation/NSBundle.h>
 
60
#include <Foundation/NSDebug.h>
 
61
#include <Foundation/NSNotification.h>
 
62
#include <Foundation/NSRunLoop.h>
 
63
#include <Foundation/NSUserDefaults.h>
 
64
#include <Foundation/NSCharacterSet.h>
 
65
#include <Foundation/NSArchiver.h>
 
66
#include <GNUstepBase/Unicode.h>
 
67
#include <AppKit/NSApplication.h>
 
68
#include <AppKit/NSPasteboard.h>
 
69
#include <AppKit/NSDragging.h>
 
70
#include <AppKit/NSEvent.h>
 
71
#include <AppKit/NSGraphics.h>
 
72
#include <AppKit/NSScroller.h>
 
73
#include <AppKit/DPSOperators.h>
 
74
 
 
75
#include "TerminalView.h"
 
76
 
 
77
#include "TerminalViewPrefs.h"
 
78
 
 
79
 
 
80
/* forkpty replacement */
 
81
#ifdef USE_FORKPTY_REPLACEMENT
 
82
#include <stdio.h> /* for stderr and perror*/
 
83
#include <errno.h> /* for int errno */
 
84
#include <fcntl.h>
 
85
#include <sys/termios.h>
 
86
#include <sys/types.h>
 
87
#include <unistd.h>
 
88
#include <stropts.h>
 
89
#include <stdlib.h>
 
90
#include <string.h>
 
91
 
 
92
#define PATH_TTY "/dev/tty"
 
93
 
 
94
int ptyMakeControllingTty(int *slaveFd, const char *slaveName)
 
95
{
 
96
    pid_t pgid;
 
97
    int   fd;
 
98
    
 
99
    if (!slaveFd || *slaveFd < 0)
 
100
    {
 
101
        perror("slaveFd invalid");
 
102
        return -1;
 
103
    }
 
104
    
 
105
    /* disconnect from the old controlling tty */
 
106
#ifdef TIOCNOTTY
 
107
    if ((fd = open(PATH_TTY, O_RDWR | O_NOCTTY)) >= 0 )
 
108
    {
 
109
        ioctl(fd, TIOCNOTTY, NULL);
 
110
        close(fd);
 
111
    }
 
112
#endif
 
113
 
 
114
 
 
115
    pgid = setsid(); /* create session and set process ID */
 
116
    if (pgid == -1)
 
117
    {
 
118
        if (errno == EPERM)
 
119
            perror("EPERM error on setsid");
 
120
    }
 
121
 
 
122
    /* Make it our controlling tty */
 
123
#ifdef TIOCSCTTY
 
124
    if (ioctl(*slaveFd, TIOCSCTTY, NULL) == -1)
 
125
        return -1;
 
126
#else
 
127
#warning TIOCSCTTY replacement
 
128
    {
 
129
        /* first terminal we open after setsid() is the controlling one */
 
130
        char *controllingTty;
 
131
        int ctr_fdes;
 
132
        
 
133
        controllingTty = ttyname(*slaveFd);
 
134
        ctr_fdes = open(controllingTty, O_RDWR);
 
135
        close(ctr_fdes);
 
136
    }
 
137
#endif /* TIOCSCTTY */
 
138
 
 
139
#if defined (TIOCSPGRP)
 
140
    ioctl (0, TIOCSPGRP, &pgid);
 
141
#else
 
142
#warning no TIOCSPGRP
 
143
    tcsetpgrp (0, pgid);
 
144
#endif
 
145
 
 
146
 
 
147
 
 
148
    if ((fd = open(slaveName, O_RDWR)) >= 0)
 
149
    {
 
150
        close(*slaveFd);
 
151
        *slaveFd = fd;
 
152
        printf("Got new filedescriptor...\n");
 
153
    }
 
154
    if ((fd = open(PATH_TTY, O_RDWR)) == -1)
 
155
        return -1;
 
156
    
 
157
    close(fd);
 
158
 
 
159
    return 0;
 
160
}
 
161
 
 
162
int openpty(int *amaster, int *aslave, char *name, const struct termios *termp, const struct winsize *winp)
 
163
{
 
164
    int fdm, fds;
 
165
    char *slaveName;
 
166
    
 
167
    fdm = open("/dev/ptmx", O_RDWR); /* open master */
 
168
    if (fdm == -1)
 
169
    {
 
170
        perror("openpty:open(master)");
 
171
        return -1;
 
172
    }
 
173
    if(grantpt(fdm))                    /* grant access to the slave */
 
174
    {
 
175
        perror("openpty:grantpt(master)");
 
176
        close(fdm);
 
177
        return -1;
 
178
    }
 
179
    if(unlockpt(fdm))                /* unlock the slave terminal */
 
180
    {
 
181
        perror("openpty:unlockpt(master)");
 
182
        close(fdm);
 
183
        return -1;
 
184
    }
 
185
    
 
186
    slaveName = ptsname(fdm);        /* get name of the slave */
 
187
    if (slaveName == NULL)
 
188
    {
 
189
        perror("openpty:ptsname(master)");
 
190
        close(fdm);
 
191
        return -1;
 
192
    }
 
193
    if (name)                        /* of name ptr not null, copy it name back */
 
194
        strcpy(name, slaveName);
 
195
    
 
196
    fds = open(slaveName, O_RDWR | O_NOCTTY); /* open slave */
 
197
    if (fds == -1)
 
198
    {
 
199
        perror("openpty:open(slave)");
 
200
        close (fdm);
 
201
        return -1;
 
202
    }
 
203
    
 
204
    /* ldterm and ttcompat are automatically pushed on the stack on some systems*/
 
205
#ifdef __SOLARIS__
 
206
    if (ioctl(fds, I_PUSH, "ptem") == -1) /* pseudo terminal module */
 
207
    {
 
208
        perror("openpty:ioctl(I_PUSH, ptem");
 
209
        close(fdm);
 
210
        close(fds);
 
211
        return -1;
 
212
    }
 
213
    if (ioctl(fds, I_PUSH, "ldterm") == -1)  /* ldterm must stay atop ptem */
 
214
    {
 
215
        perror("forkpty:ioctl(I_PUSH, ldterm");
 
216
        close(fdm);
 
217
        close(fds);
 
218
        return -1;
 
219
    }
 
220
#endif
 
221
    
 
222
    /* set terminal parameters if present */
 
223
    if (termp)
 
224
        ioctl(fds, TCSETS, termp);
 
225
    if (winp)
 
226
        ioctl(fds, TIOCSWINSZ, winp);
 
227
    
 
228
    *amaster = fdm;
 
229
    *aslave = fds;
 
230
    return 0;
 
231
}
 
232
 
 
233
int forkpty (int *amaster, char *slaveName, const struct termios *termp, const struct winsize *winp)
 
234
{
 
235
    int fdm, fds; /* master and slave file descriptors */
 
236
    pid_t pid;
 
237
    
 
238
    if (openpty(&fdm, &fds, slaveName, termp, winp) == -1)
 
239
    {
 
240
        perror("forkpty:openpty()");
 
241
        return -1;
 
242
    }
 
243
 
 
244
 
 
245
    pid = fork();
 
246
    if (pid == -1)
 
247
    {
 
248
        /* error */
 
249
        perror("forkpty:fork()");
 
250
        close(fdm);
 
251
        close(fds);
 
252
        return -1;
 
253
    } else if (pid == 0)
 
254
    {   
 
255
        /* child */
 
256
        ptyMakeControllingTty(&fds, slaveName);
 
257
        if (fds != STDIN_FILENO && dup2(fds, STDIN_FILENO) == -1)
 
258
            perror("error duplicationg stdin");
 
259
        if (fds != STDOUT_FILENO && dup2(fds, STDOUT_FILENO) == -1)
 
260
            perror("error duplicationg stdout");
 
261
        if (fds != STDERR_FILENO && dup2(fds, STDERR_FILENO) == -1)
 
262
            perror("error duplicationg stderr");
 
263
        
 
264
        if (fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO)
 
265
            close(fds);
 
266
        
 
267
        
 
268
        close (fdm);
 
269
    } else
 
270
    {
 
271
        /* father */
 
272
        close (fds);
 
273
        *amaster = fdm;
 
274
    }
 
275
    return pid;
 
276
}
 
277
 
 
278
#endif /* forpkty replacement */
 
279
 
 
280
/* TODO */
 
281
@interface NSView (unlockfocus)
 
282
-(void) unlockFocusNeedsFlush: (BOOL)flush;
 
283
@end
 
284
 
 
285
 
 
286
NSString
 
287
        *TerminalViewBecameIdleNotification=@"TerminalViewBecameIdle",
 
288
        *TerminalViewBecameNonIdleNotification=@"TerminalViewBecameNonIdle",
 
289
 
 
290
        *TerminalViewTitleDidChangeNotification=@"TerminalViewTitleDidChange";
 
291
 
 
292
 
 
293
 
 
294
@interface TerminalView (scrolling)
 
295
-(void) _updateScroller;
 
296
-(void) _scrollTo: (int)new_scroll  update: (BOOL)update;
 
297
-(void) setScroller: (NSScroller *)sc;
 
298
@end
 
299
 
 
300
@interface TerminalView (selection)
 
301
-(void) _clearSelection;
 
302
@end
 
303
 
 
304
@interface TerminalView (input) <RunLoopEvents>
 
305
-(void) closeProgram;
 
306
-(void) runShell;
 
307
-(void) runProgram: (NSString *)path
 
308
        withArguments: (NSArray *)args
 
309
        initialInput: (NSString *)d;
 
310
@end
 
311
 
 
312
 
 
313
/**
 
314
TerminalScreen protocol implementation and rendering methods
 
315
**/
 
316
 
 
317
@implementation TerminalView (display)
 
318
 
 
319
#define ADD_DIRTY(ax0,ay0,asx,asy) do { \
 
320
                if (dirty.x0==-1) \
 
321
                { \
 
322
                        dirty.x0=(ax0); \
 
323
                        dirty.y0=(ay0); \
 
324
                        dirty.x1=(ax0)+(asx); \
 
325
                        dirty.y1=(ay0)+(asy); \
 
326
                } \
 
327
                else \
 
328
                { \
 
329
                        if (dirty.x0>(ax0)) dirty.x0=(ax0); \
 
330
                        if (dirty.y0>(ay0)) dirty.y0=(ay0); \
 
331
                        if (dirty.x1<(ax0)+(asx)) dirty.x1=(ax0)+(asx); \
 
332
                        if (dirty.y1<(ay0)+(asy)) dirty.y1=(ay0)+(asy); \
 
333
                } \
 
334
        } while (0)
 
335
 
 
336
 
 
337
#define SCREEN(x,y) (screen[(y)*sx+(x)])
 
338
 
 
339
 
 
340
/* handle accumulated pending scrolls with a single composite */
 
341
-(void) _handlePendingScroll: (BOOL)lockFocus
 
342
{
 
343
        float x0,y0,w,h,dx,dy;
 
344
 
 
345
        if (!pending_scroll)
 
346
                return;
 
347
 
 
348
        if (pending_scroll>=sy || pending_scroll<=-sy)
 
349
        {
 
350
                pending_scroll=0;
 
351
                return;
 
352
        }
 
353
 
 
354
        NSDebugLLog(@"draw",@"_handlePendingScroll %i %i",pending_scroll,lockFocus);
 
355
 
 
356
        dx=x0=0;
 
357
        w=fx*sx;
 
358
 
 
359
        if (pending_scroll>0)
 
360
        {
 
361
                y0=0;
 
362
                h=(sy-pending_scroll)*fy;
 
363
                dy=pending_scroll*fy;
 
364
                y0=sy*fy-y0-h;
 
365
                dy=sy*fy-dy-h;
 
366
        }
 
367
        else
 
368
        {
 
369
                pending_scroll=-pending_scroll;
 
370
 
 
371
                y0=pending_scroll*fy;
 
372
                h=(sy-pending_scroll)*fy;
 
373
                dy=0;
 
374
                y0=sy*fy-y0-h;
 
375
                dy=sy*fy-dy-h;
 
376
        }
 
377
 
 
378
        if (lockFocus)
 
379
                [self lockFocus];
 
380
        DPScomposite(GSCurrentContext(),border_x+x0,border_y+y0,w,h,
 
381
                [self gState],border_x+dx,border_y+dy,NSCompositeCopy);
 
382
        if (lockFocus)
 
383
                [self unlockFocusNeedsFlush: NO];
 
384
 
 
385
        num_scrolls++;
 
386
        pending_scroll=0;
 
387
}
 
388
 
 
389
 
 
390
static int total_draw=0;
 
391
 
 
392
 
 
393
static const float col_h[8]={  0,240,120,180,  0,300, 60,  0};
 
394
static const float col_s[8]={0.0,1.0,1.0,1.0,1.0,1.0,1.0,0.0};
 
395
 
 
396
static void set_background(NSGraphicsContext *gc,
 
397
        unsigned char color,unsigned char in)
 
398
{
 
399
        float bh,bs,bb;
 
400
        int bg=color>>4;
 
401
 
 
402
        if (bg==0)
 
403
                bb=0.0;
 
404
        else if (bg>=8)
 
405
                bg-=8,bb=1.0;
 
406
        else
 
407
                bb=0.6;
 
408
        bs=col_s[bg];
 
409
        bh=col_h[bg]/360.0;
 
410
 
 
411
        DPSsethsbcolor(gc,bh,bs,bb);
 
412
}
 
413
 
 
414
static void set_foreground(NSGraphicsContext *gc,
 
415
        unsigned char color,unsigned char in)
 
416
{
 
417
        int fg=color;
 
418
        float h,s,b;
 
419
 
 
420
        if (fg>=8)
 
421
        {
 
422
                in++;
 
423
                fg-=8;
 
424
        }
 
425
 
 
426
        if (fg==0)
 
427
        {
 
428
                if (in==2)
 
429
                        b=0.4;
 
430
                else
 
431
                        b=0.0;
 
432
        }
 
433
        else if (in==0)
 
434
                b=0.6;
 
435
        else if (in==1)
 
436
                b=0.8;
 
437
        else
 
438
                b=1.0;
 
439
 
 
440
        h=col_h[fg]/360.0;
 
441
        s=col_s[fg];
 
442
        if (in==2)
 
443
                s*=0.75;
 
444
 
 
445
        DPSsethsbcolor(gc,h,s,b);
 
446
}
 
447
 
 
448
 
 
449
-(void) drawRect: (NSRect)r
 
450
{
 
451
        int ix,iy;
 
452
        unsigned char buf[8];
 
453
        NSGraphicsContext *cur=GSCurrentContext();
 
454
        int x0,y0,x1,y1;
 
455
        NSFont *f,*current_font=nil;
 
456
 
 
457
        int encoding;
 
458
 
 
459
 
 
460
        NSDebugLLog(@"draw",@"drawRect: (%g %g)+(%g %g) %i\n",
 
461
                r.origin.x,r.origin.y,r.size.width,r.size.height,
 
462
                draw_all);
 
463
 
 
464
        if (pending_scroll)
 
465
                [self _handlePendingScroll: NO];
 
466
 
 
467
        /* draw the black border around the view if needed*/
 
468
        {
 
469
                float a,b;
 
470
                DPSsetgray(cur,0.0);
 
471
                if (r.origin.x<border_x)
 
472
                        DPSrectfill(cur,r.origin.x,r.origin.y,border_x-r.origin.x,r.size.height);
 
473
                if (r.origin.y<border_y)
 
474
                        DPSrectfill(cur,r.origin.x,r.origin.y,r.size.width,border_y-r.origin.y);
 
475
 
 
476
                a=border_x+sx*fx;
 
477
                b=r.origin.x+r.size.width;
 
478
                if (b>a)
 
479
                        DPSrectfill(cur,a,r.origin.y,b-a,r.size.height);
 
480
                a=border_y+sy*fy;
 
481
                b=r.origin.y+r.size.height;
 
482
                if (b>a)
 
483
                        DPSrectfill(cur,r.origin.x,a,r.size.width,b-a);
 
484
        }
 
485
 
 
486
        /* figure out what character cells might need redrawing */
 
487
        r.origin.x-=border_x;
 
488
        r.origin.y-=border_y;
 
489
 
 
490
        x0=floor(r.origin.x/fx);
 
491
        x1=ceil((r.origin.x+r.size.width)/fx);
 
492
        if (x0<0) x0=0;
 
493
        if (x1>=sx) x1=sx;
 
494
 
 
495
        y1=floor(r.origin.y/fy);
 
496
        y0=ceil((r.origin.y+r.size.height)/fy);
 
497
        y0=sy-y0;
 
498
        y1=sy-y1;
 
499
        if (y0<0) y0=0;
 
500
        if (y1>=sy) y1=sy;
 
501
 
 
502
        NSDebugLLog(@"draw",@"dirty (%i %i)-(%i %i)\n",x0,y0,x1,y1);
 
503
 
 
504
        draw_cursor=draw_cursor || draw_all ||
 
505
                    (SCREEN(cursor_x,cursor_y).attr&0x80)!=0;
 
506
 
 
507
        {
 
508
                int ry;
 
509
                screen_char_t *ch;
 
510
                float scr_y,scr_x,start_x;
 
511
 
 
512
                /* setting the color is slow, so we try to avoid it */
 
513
                unsigned char l_color,l_attr,color;
 
514
 
 
515
                /* Fill the background of dirty cells. Since the background doesn't
 
516
                change that often, runs of dirty cells with the same background color
 
517
                are combined and drawn with a single rectfill. */
 
518
                l_color=0;
 
519
                l_attr=0;
 
520
                set_foreground(cur,l_color,l_attr);
 
521
                for (iy=y0;iy<y1;iy++)
 
522
                {
 
523
                        ry=iy+current_scroll;
 
524
                        if (ry>=0)
 
525
                                ch=&SCREEN(x0,ry);
 
526
                        else
 
527
                                ch=&sbuf[x0+(max_scrollback+ry)*sx];
 
528
 
 
529
                        scr_y=(sy-1-iy)*fy+border_y;
 
530
/*
 
531
#define R(scr_x,scr_y,fx,fy) \
 
532
                                DPSgsave(cur); \
 
533
                                DPSsetgray(cur,0.0); \
 
534
                                DPSrectfill(cur,scr_x,scr_y,fx,fy); \
 
535
                                DPSgrestore(cur); \
 
536
                                DPSrectstroke(cur,scr_x,scr_y,fx,fy); \
 
537
*/
 
538
 
 
539
/* ~400 cycles/cell on average */
 
540
#define R(scr_x,scr_y,fx,fy) DPSrectfill(cur,scr_x,scr_y,fx,fy)
 
541
                        start_x=-1;
 
542
                        for (ix=x0;ix<x1;ix++,ch++)
 
543
                        {
 
544
                                if (!draw_all && !(ch->attr&0x80))
 
545
                                {
 
546
                                        if (start_x!=-1)
 
547
                                        {
 
548
                                                scr_x=ix*fx+border_x;
 
549
                                                R(start_x,scr_y,scr_x-start_x,fy);
 
550
                                                start_x=-1;
 
551
                                        }
 
552
                                        continue;
 
553
                                }
 
554
 
 
555
                                scr_x=ix*fx+border_x;
 
556
 
 
557
                                if (ch->attr&0x8)
 
558
                                {
 
559
                                        color=ch->color&0xf;
 
560
                                        if (ch->attr&0x40) color^=0xf;
 
561
                                        if (color!=l_color || (ch->attr&0x03)!=l_attr)
 
562
                                        {
 
563
                                                if (start_x!=-1)
 
564
                                                {
 
565
                                                        R(start_x,scr_y,scr_x-start_x,fy);
 
566
                                                        start_x=scr_x;
 
567
                                                }
 
568
 
 
569
                                                l_color=color;
 
570
                                                l_attr=ch->attr&0x03;
 
571
                                                set_foreground(cur,l_color,l_attr);
 
572
                                        }
 
573
                                }
 
574
                                else
 
575
                                {
 
576
                                        color=ch->color&0xf0;
 
577
                                        if (ch->attr&0x40) color^=0xf0;
 
578
                                        if (color!=l_color)
 
579
                                        {
 
580
                                                if (start_x!=-1)
 
581
                                                {
 
582
                                                        R(start_x,scr_y,scr_x-start_x,fy);
 
583
                                                        start_x=scr_x;
 
584
                                                }
 
585
 
 
586
                                                l_color=color;
 
587
                                                l_attr=ch->attr&0x03;
 
588
                                                set_background(cur,l_color,l_attr);
 
589
                                        }
 
590
                                }
 
591
 
 
592
                                if (start_x==-1)
 
593
                                        start_x=scr_x;
 
594
                        }
 
595
 
 
596
                        if (start_x!=-1)
 
597
                        {
 
598
                                scr_x=ix*fx+border_x;
 
599
                                R(start_x,scr_y,scr_x-start_x,fy);
 
600
                        }
 
601
                }
 
602
 
 
603
                /* now draw any dirty characters */
 
604
                for (iy=y0;iy<y1;iy++)
 
605
                {
 
606
                        ry=iy+current_scroll;
 
607
                        if (ry>=0)
 
608
                                ch=&SCREEN(x0,ry);
 
609
                        else
 
610
                                ch=&sbuf[x0+(max_scrollback+ry)*sx];
 
611
 
 
612
                        scr_y=(sy-1-iy)*fy+border_y;
 
613
 
 
614
                        for (ix=x0;ix<x1;ix++,ch++)
 
615
                        {
 
616
                                if (!draw_all && !(ch->attr&0x80))
 
617
                                        continue;
 
618
 
 
619
                                ch->attr&=0x7f;
 
620
 
 
621
                                scr_x=ix*fx+border_x;
 
622
 
 
623
                                /* ~1700 cycles/change */
 
624
                                if (ch->attr&0x02 || (ch->ch!=0 && ch->ch!=32))
 
625
                                {
 
626
                                        if (!(ch->attr&0x8))
 
627
                                        {
 
628
                                                color=ch->color&0xf;
 
629
                                                if (ch->attr&0x40) color^=0xf;
 
630
                                                if (color!=l_color || (ch->attr&0x03)!=l_attr)
 
631
                                                {
 
632
                                                        l_color=color;
 
633
                                                        l_attr=ch->attr&0x03;
 
634
                                                        set_foreground(cur,l_color,l_attr);
 
635
                                                }
 
636
                                        }
 
637
                                        else
 
638
                                        {
 
639
                                                color=ch->color&0xf0;
 
640
                                                if (ch->attr&0x40) color^=0xf0;
 
641
                                                if (color!=l_color)
 
642
                                                {
 
643
                                                        l_color=color;
 
644
                                                        l_attr=ch->attr&0x03;
 
645
                                                        set_background(cur,l_color,l_attr);
 
646
                                                }
 
647
                                        }
 
648
                                }
 
649
 
 
650
                                if (ch->ch!=0 && ch->ch!=32 && ch->ch!=MULTI_CELL_GLYPH)
 
651
                                {
 
652
                                        total_draw++;
 
653
                                        if ((ch->attr&3)==2)
 
654
                                        {
 
655
                                                encoding=boldFont_encoding;
 
656
                                                f=boldFont;
 
657
                                        }
 
658
                                        else
 
659
                                        {
 
660
                                                encoding=font_encoding;
 
661
                                                f=font;
 
662
                                        }
 
663
                                        if (f!=current_font)
 
664
                                        {
 
665
                                        /* ~190 cycles/change */
 
666
                                                [f set];
 
667
                                                current_font=f;
 
668
                                        }
 
669
 
 
670
                                        /* we short-circuit utf8 for performance with back-art */
 
671
                                        /* TODO: short-circuit latin1 too? */
 
672
                                        if (encoding==NSUTF8StringEncoding)
 
673
                                        {
 
674
                                                unichar uch=ch->ch;
 
675
                                                if (uch>=0x800)
 
676
                                                {
 
677
                                                        buf[2]=(uch&0x3f)|0x80;
 
678
                                                        uch>>=6;
 
679
                                                        buf[1]=(uch&0x3f)|0x80;
 
680
                                                        uch>>=6;
 
681
                                                        buf[0]=(uch&0x0f)|0xe0;
 
682
                                                        buf[3]=0;
 
683
                                                }
 
684
                                                else if (uch>=0x80)
 
685
                                                {
 
686
                                                        buf[1]=(uch&0x3f)|0x80;
 
687
                                                        uch>>=6;
 
688
                                                        buf[0]=(uch&0x1f)|0xc0;
 
689
                                                        buf[2]=0;
 
690
                                                }
 
691
                                                else
 
692
                                                {
 
693
                                                        buf[0]=uch;
 
694
                                                        buf[1]=0;
 
695
                                                }
 
696
                                        }
 
697
                                        else
 
698
                                        {
 
699
                                                unichar uch=ch->ch;
 
700
                                                if (uch<=0x80)
 
701
                                                {
 
702
                                                        buf[0]=uch;
 
703
                                                        buf[1]=0;
 
704
                                                }
 
705
                                                else
 
706
                                                {
 
707
                                                        unsigned char *pbuf=buf;
 
708
                                                        int dlen=sizeof(buf)-1;
 
709
                                                        GSFromUnicode(&pbuf,&dlen,&uch,1,encoding,NULL,GSUniTerminate);
 
710
                                                }
 
711
                                        }
 
712
                                        /* ~580 cycles */
 
713
                                        DPSmoveto(cur,scr_x+fx0,scr_y+fy0);
 
714
                                        /* baseline here for mc-case 0.65 */
 
715
                                        /* ~3800 cycles */
 
716
                                        DPSshow(cur,buf);
 
717
 
 
718
                                        /* ~95 cycles to ARTGState -DPSshow:... */
 
719
                                        /* ~343 cycles to isEmpty */
 
720
                                        /* ~593 cycles to currentpoint */
 
721
                                        /* ~688 cycles to transform */
 
722
                                        /* ~1152 cycles to FTFont -drawString:... */
 
723
                                        /* ~1375 cycles to -drawString:... setup */
 
724
                                        /* ~1968 cycles cmap lookup */
 
725
                                        /* ~2718 cycles sbit lookup */
 
726
                                        /* ~~2750 cycles blit setup */
 
727
                                        /* ~3140 cycles blit loop, empty call */
 
728
                                        /* ~3140 cycles blit loop, setup */
 
729
                                        /* ~3325 cycles blit loop, no write */
 
730
                                        /* ~3800 cycles total */
 
731
                                }
 
732
 
 
733
                                /* underline */
 
734
                                if (ch->attr&0x4)
 
735
                                        DPSrectfill(cur,scr_x,scr_y,fx,1);
 
736
                        }
 
737
                }
 
738
        }
 
739
 
 
740
        if (draw_cursor)
 
741
        {
 
742
                float x,y;
 
743
                [[TerminalViewDisplayPrefs cursorColor] set];
 
744
 
 
745
                x=cursor_x*fx+border_x;
 
746
                y=(sy-1-cursor_y+current_scroll)*fy+border_y;
 
747
 
 
748
                switch ([TerminalViewDisplayPrefs cursorStyle])
 
749
                {
 
750
                case CURSOR_LINE:
 
751
                        DPSrectfill(cur,x,y,fx,fy*0.1);
 
752
                        break;
 
753
                case CURSOR_BLOCK_STROKE:
 
754
                        DPSrectstroke(cur,x+0.5,y+0.5,fx-1.0,fy-1.0);
 
755
                        break;
 
756
                case CURSOR_BLOCK_FILL:
 
757
                        DPSrectfill(cur,x,y,fx,fy);
 
758
                        break;
 
759
                case CURSOR_BLOCK_INVERT:
 
760
                        DPScompositerect(cur,x,y,fx,fy,
 
761
                                NSCompositeHighlight);
 
762
                        break;
 
763
                }
 
764
                draw_cursor=NO;
 
765
        }
 
766
 
 
767
        NSDebugLLog(@"draw",@"total_draw=%i",total_draw);
 
768
 
 
769
        draw_all=1;
 
770
}
 
771
 
 
772
-(BOOL) isOpaque
 
773
{
 
774
        return YES;
 
775
}
 
776
 
 
777
-(void) setNeedsDisplayInRect: (NSRect)r
 
778
{
 
779
        draw_all=2;
 
780
        [super setNeedsDisplayInRect: r];
 
781
}
 
782
 
 
783
-(void) setNeedsLazyDisplayInRect: (NSRect)r
 
784
{
 
785
        if (draw_all==1)
 
786
                draw_all=0;
 
787
        [super setNeedsDisplayInRect: r];
 
788
}
 
789
 
 
790
 
 
791
-(void) benchmark: (id)sender
 
792
{
 
793
        int i;
 
794
        double t1,t2;
 
795
        NSRect r=[self frame];
 
796
        t1=[NSDate timeIntervalSinceReferenceDate];
 
797
        total_draw=0;
 
798
        for (i=0;i<100;i++)
 
799
        {
 
800
                draw_all=2;
 
801
                [self lockFocus];
 
802
                [self drawRect: r];
 
803
                [self unlockFocusNeedsFlush: NO];
 
804
        }
 
805
        t2=[NSDate timeIntervalSinceReferenceDate];
 
806
        t2-=t1;
 
807
        fprintf(stderr,"%8.4f  %8.5f/redraw   total_draw=%i\n",t2,t2/i,total_draw);
 
808
}
 
809
 
 
810
 
 
811
-(void) ts_setTitle: (NSString *)new_title  type: (int)title_type
 
812
{
 
813
        NSDebugLLog(@"ts",@"setTitle: %@  type: %i",new_title,title_type);
 
814
        if (title_type==1 || title_type==0)
 
815
                ASSIGN(title_miniwindow,new_title);
 
816
        if (title_type==2 || title_type==0)
 
817
                ASSIGN(title_window,new_title);
 
818
        [[NSNotificationCenter defaultCenter]
 
819
                postNotificationName: TerminalViewTitleDidChangeNotification
 
820
                object: self];
 
821
}
 
822
 
 
823
 
 
824
-(void) ts_goto: (int)x:(int)y
 
825
{
 
826
        NSDebugLLog(@"ts",@"goto: %i:%i",x,y);
 
827
        cursor_x=x;
 
828
        cursor_y=y;
 
829
        if (cursor_x>=sx) cursor_x=sx-1;
 
830
        if (cursor_x<0) cursor_x=0;
 
831
        if (cursor_y>=sy) cursor_y=sy-1;
 
832
        if (cursor_y<0) cursor_y=0;
 
833
}
 
834
 
 
835
-(void) ts_putChar: (screen_char_t)ch  count: (int)c  at: (int)x:(int)y
 
836
{
 
837
        int i;
 
838
        screen_char_t *s;
 
839
 
 
840
        NSDebugLLog(@"ts",@"putChar: '%c' %02x %02x count: %i at: %i:%i",
 
841
                ch.ch,ch.color,ch.attr,c,x,y);
 
842
 
 
843
        if (y<0 || y>=sy) return;
 
844
        if (x+c>sx)
 
845
                c=sx-x;
 
846
        if (x<0)
 
847
        {
 
848
                c-=x;
 
849
                x=0;
 
850
        }
 
851
        s=&SCREEN(x,y);
 
852
        ch.attr|=0x80;
 
853
        for (i=0;i<c;i++)
 
854
                *s++=ch;
 
855
        ADD_DIRTY(x,y,c,1);
 
856
}
 
857
 
 
858
-(void) ts_putChar: (screen_char_t)ch  count: (int)c  offset: (int)ofs
 
859
{
 
860
        int i;
 
861
        screen_char_t *s;
 
862
 
 
863
        NSDebugLLog(@"ts",@"putChar: '%c' %02x %02x count: %i offset: %i",
 
864
                ch.ch,ch.color,ch.attr,c,ofs);
 
865
 
 
866
        if (ofs+c>sx*sy)
 
867
                c=sx*sy-ofs;
 
868
        if (ofs<0)
 
869
        {
 
870
                c-=ofs;
 
871
                ofs=0;
 
872
        }
 
873
        s=&SCREEN(ofs,0);
 
874
        ch.attr|=0x80;
 
875
        for (i=0;i<c;i++)
 
876
                *s++=ch;
 
877
        ADD_DIRTY(0,0,sx,sy); /* TODO */
 
878
}
 
879
 
 
880
-(void) ts_scrollUp: (int)t:(int)b  rows: (int)nr  save: (BOOL)save
 
881
{
 
882
        screen_char_t *d, *s;
 
883
 
 
884
        NSDebugLLog(@"ts",@"scrollUp: %i:%i  rows: %i  save: %i",
 
885
                t,b,nr,save);
 
886
 
 
887
        if (save && t==0 && b==sy) /* TODO? */
 
888
        {
 
889
                int num;
 
890
                if (nr<max_scrollback)
 
891
                {
 
892
                        memmove(sbuf,&sbuf[sx*nr],sizeof(screen_char_t)*sx*(max_scrollback-nr));
 
893
                        num=nr;
 
894
                }
 
895
                else
 
896
                        num=max_scrollback;
 
897
 
 
898
                if (num<sy)
 
899
                {
 
900
                        memmove(&sbuf[sx*(max_scrollback-num)],screen,num*sx*sizeof(screen_char_t));
 
901
                }
 
902
                else
 
903
                {
 
904
                        memmove(&sbuf[sx*(max_scrollback-num)],screen,sy*sx*sizeof(screen_char_t));
 
905
 
 
906
                        /* TODO: should this use video_erase_char? */
 
907
                        memset(&sbuf[sx*(max_scrollback-num+sy)],0,sx*(num-sy)*sizeof(screen_char_t));
 
908
                }
 
909
                sb_length+=num;
 
910
                if (sb_length>max_scrollback)
 
911
                        sb_length=max_scrollback;
 
912
        }
 
913
 
 
914
        if (t+nr >= b)
 
915
                nr = b - t - 1;
 
916
        if (b > sy || t >= b || nr < 1)
 
917
                return;
 
918
        d = &SCREEN(0,t);
 
919
        s = &SCREEN(0,t+nr);
 
920
 
 
921
        if (current_y>=t && current_y<=b)
 
922
        {
 
923
                SCREEN(current_x,current_y).attr|=0x80;
 
924
                draw_cursor=YES;
 
925
                /*
 
926
                TODO: does this properly handle the case when the cursor is in
 
927
                an area that gets scrolled 'over'?
 
928
 
 
929
                now it does, but not in an optimal way. handling of this could be
 
930
                optimized in all scrolling methods, but it probably won't make
 
931
                much difference
 
932
                */
 
933
        }
 
934
        memmove(d, s, (b-t-nr) * sx * sizeof(screen_char_t));
 
935
        if (!current_scroll)
 
936
        {
 
937
                if (t==0 && b==sy)
 
938
                {
 
939
                        pending_scroll-=nr;
 
940
                }
 
941
                else
 
942
                {
 
943
                        float x0,y0,w,h,dx,dy;
 
944
 
 
945
                        if (pending_scroll)
 
946
                                [self _handlePendingScroll: YES];
 
947
 
 
948
                        x0=0;
 
949
                        w=fx*sx;
 
950
                        y0=(t+nr)*fy;
 
951
                        h=(b-t-nr)*fy;
 
952
                        dx=0;
 
953
                        dy=t*fy;
 
954
                        y0=sy*fy-y0-h;
 
955
                        dy=sy*fy-dy-h;
 
956
                        [self lockFocus];
 
957
                        DPScomposite(GSCurrentContext(),border_x+x0,border_y+y0,w,h,
 
958
                                [self gState],border_x+dx,border_y+dy,NSCompositeCopy);
 
959
                        [self unlockFocusNeedsFlush: NO];
 
960
                        num_scrolls++;
 
961
                }
 
962
        }
 
963
        ADD_DIRTY(0,t,sx,b-t);
 
964
}
 
965
 
 
966
-(void) ts_scrollDown: (int)t:(int)b  rows: (int)nr
 
967
{
 
968
        screen_char_t *s;
 
969
        unsigned int step;
 
970
 
 
971
        NSDebugLLog(@"ts",@"scrollDown: %i:%i  rows: %i",
 
972
                t,b,nr);
 
973
 
 
974
        if (t+nr >= b)
 
975
                nr = b - t - 1;
 
976
        if (b > sy || t >= b || nr < 1)
 
977
                return;
 
978
        s = &SCREEN(0,t);
 
979
        step = sx * nr;
 
980
        if (current_y>=t && current_y<=b)
 
981
        {
 
982
                SCREEN(current_x,current_y).attr|=0x80;
 
983
                draw_cursor=YES;
 
984
        }
 
985
        memmove(s + step, s, (b-t-nr)*sx*sizeof(screen_char_t));
 
986
        if (!current_scroll)
 
987
        {
 
988
                if (t==0 && b==sy)
 
989
                {
 
990
                        pending_scroll+=nr;
 
991
                }
 
992
                else
 
993
                {
 
994
                        float x0,y0,w,h,dx,dy;
 
995
 
 
996
                        if (pending_scroll)
 
997
                                [self _handlePendingScroll: YES];
 
998
 
 
999
                        x0=0;
 
1000
                        w=fx*sx;
 
1001
                        y0=(t)*fy;
 
1002
                        h=(b-t-nr)*fy;
 
1003
                        dx=0;
 
1004
                        dy=(t+nr)*fy;
 
1005
                        y0=sy*fy-y0-h;
 
1006
                        dy=sy*fy-dy-h;
 
1007
                        [self lockFocus];
 
1008
                        DPScomposite(GSCurrentContext(),border_x+x0,border_y+y0,w,h,
 
1009
                                [self gState],border_x+dx,border_y+dy,NSCompositeCopy);
 
1010
                        [self unlockFocusNeedsFlush: NO];
 
1011
                        num_scrolls++;
 
1012
                }
 
1013
        }
 
1014
        ADD_DIRTY(0,t,sx,b-t);
 
1015
}
 
1016
 
 
1017
-(void) ts_shiftRow: (int)y  at: (int)x0  delta: (int)delta
 
1018
{
 
1019
        screen_char_t *s,*d;
 
1020
        int x1,c;
 
1021
        NSDebugLLog(@"ts",@"shiftRow: %i  at: %i  delta: %i",
 
1022
                y,x0,delta);
 
1023
 
 
1024
        if (y<0 || y>=sy) return;
 
1025
        if (x0<0 || x0>=sx) return;
 
1026
 
 
1027
        if (current_y==y)
 
1028
        {
 
1029
                SCREEN(current_x,current_y).attr|=0x80;
 
1030
                draw_cursor=YES;
 
1031
        }
 
1032
 
 
1033
        s=&SCREEN(x0,y);
 
1034
        x1=x0+delta;
 
1035
        c=sx-x0;
 
1036
        if (x1<0)
 
1037
        {
 
1038
                x0-=x1;
 
1039
                c+=x1;
 
1040
                x1=0;
 
1041
        }
 
1042
        if (x1+c>sx)
 
1043
                c=sx-x1;
 
1044
        d=&SCREEN(x1,y);
 
1045
        memmove(d,s,sizeof(screen_char_t)*c);
 
1046
        if (!current_scroll)
 
1047
        {
 
1048
                float cx0,y0,w,h,dx,dy;
 
1049
 
 
1050
                if (pending_scroll)
 
1051
                        [self _handlePendingScroll: YES];
 
1052
 
 
1053
                cx0=x0*fx;
 
1054
                w=fx*c;
 
1055
                dx=x1*fx;
 
1056
 
 
1057
                y0=y*fy;
 
1058
                h=fy;
 
1059
                dy=y0;
 
1060
 
 
1061
                y0=sy*fy-y0-h;
 
1062
                dy=sy*fy-dy-h;
 
1063
                [self lockFocus];
 
1064
                DPScomposite(GSCurrentContext(),border_x+cx0,border_y+y0,w,h,
 
1065
                        [self gState],border_x+dx,border_y+dy,NSCompositeCopy);
 
1066
                [self unlockFocusNeedsFlush: NO];
 
1067
                num_scrolls++;
 
1068
        }
 
1069
        ADD_DIRTY(0,y,sx,1);
 
1070
}
 
1071
 
 
1072
-(screen_char_t) ts_getCharAt: (int)x:(int)y
 
1073
{
 
1074
        NSDebugLLog(@"ts",@"getCharAt: %i:%i",x,y);
 
1075
        return SCREEN(x,y);
 
1076
}
 
1077
 
 
1078
 
 
1079
-(void) addDataToWriteBuffer: (const char *)data
 
1080
        length: (int)len
 
1081
{
 
1082
        if (!len)
 
1083
                return;
 
1084
 
 
1085
        if (!write_buf_len)
 
1086
        {
 
1087
                [[NSRunLoop currentRunLoop]
 
1088
                        addEvent: (void *)master_fd
 
1089
                        type: ET_WDESC
 
1090
                        watcher: self
 
1091
                        forMode: NSDefaultRunLoopMode];
 
1092
        }
 
1093
 
 
1094
        if (write_buf_len+len>write_buf_size)
 
1095
        {
 
1096
                /* Round up to nearest multiple of 512 bytes. */
 
1097
                write_buf_size=(write_buf_len+len+511)&~511;
 
1098
                write_buf=realloc(write_buf,write_buf_size);
 
1099
        }
 
1100
        memcpy(&write_buf[write_buf_len],data,len);
 
1101
        write_buf_len+=len;
 
1102
}
 
1103
 
 
1104
-(void) ts_sendCString: (const char *)msg
 
1105
{
 
1106
        [self ts_sendCString: msg  length: strlen(msg)];
 
1107
}
 
1108
-(void) ts_sendCString: (const char *)msg  length: (int)len
 
1109
{
 
1110
        int l;
 
1111
        if (master_fd==-1)
 
1112
                return;
 
1113
 
 
1114
        if (write_buf_len)
 
1115
        {
 
1116
                [self addDataToWriteBuffer: msg  length: len];
 
1117
                return;
 
1118
        }
 
1119
 
 
1120
        l=write(master_fd,msg,len);
 
1121
        if (l!=len)
 
1122
        {
 
1123
                if (errno!=EAGAIN)
 
1124
                        NSLog(_(@"Unexpected error while writing: %m."));
 
1125
                if (l<0)
 
1126
                        l=0;
 
1127
                [self addDataToWriteBuffer: &msg[l]  length: len-l];
 
1128
        }
 
1129
}
 
1130
 
 
1131
 
 
1132
-(BOOL) useMultiCellGlyphs
 
1133
{
 
1134
        return use_multi_cell_glyphs;
 
1135
}
 
1136
 
 
1137
-(int) relativeWidthOfCharacter: (unichar)ch
 
1138
{
 
1139
        int s;
 
1140
        if (!use_multi_cell_glyphs)
 
1141
                return 1;
 
1142
        s=ceil([font boundingRectForGlyph: ch].size.width/fx);
 
1143
        if (s<1)
 
1144
                return 1;
 
1145
        return s;
 
1146
}
 
1147
 
 
1148
 
 
1149
-(void) viewPrefsDidChange: (NSNotification *)n
 
1150
{
 
1151
        /* TODO: handle font changes? */
 
1152
        [self setNeedsDisplay: YES];
 
1153
}
 
1154
 
 
1155
@end
 
1156
 
 
1157
 
 
1158
/**
 
1159
Scrolling
 
1160
**/
 
1161
 
 
1162
@implementation TerminalView (scrolling)
 
1163
 
 
1164
-(void) _updateScroller
 
1165
{
 
1166
        if (sb_length)
 
1167
        {
 
1168
                [scroller setEnabled: YES];
 
1169
                [scroller setFloatValue: (current_scroll+sb_length)/(float)(sb_length)
 
1170
                        knobProportion: sy/(float)(sy+sb_length)];
 
1171
        }
 
1172
        else
 
1173
        {
 
1174
                [scroller setEnabled: NO];
 
1175
        }
 
1176
}
 
1177
 
 
1178
-(void) _scrollTo: (int)new_scroll  update: (BOOL)update
 
1179
{
 
1180
        if (new_scroll>0)
 
1181
                new_scroll=0;
 
1182
        if (new_scroll<-sb_length)
 
1183
                new_scroll=-sb_length;
 
1184
 
 
1185
        if (new_scroll==current_scroll)
 
1186
                return;
 
1187
        current_scroll=new_scroll;
 
1188
 
 
1189
        if (update)
 
1190
        {
 
1191
                [self _updateScroller];
 
1192
        }
 
1193
 
 
1194
        [self setNeedsDisplay: YES];
 
1195
}
 
1196
 
 
1197
-(void) scrollWheel: (NSEvent *)e
 
1198
{
 
1199
        float delta=[e deltaY];
 
1200
        int new_scroll;
 
1201
        int mult;
 
1202
 
 
1203
        if ([e modifierFlags]&NSShiftKeyMask)
 
1204
                mult=1;
 
1205
        else if ([e modifierFlags]&NSControlKeyMask)
 
1206
                mult=sy;
 
1207
        else
 
1208
                mult=5;
 
1209
 
 
1210
        new_scroll=current_scroll-delta*mult;
 
1211
        [self _scrollTo: new_scroll  update: YES];
 
1212
}
 
1213
 
 
1214
-(void) _updateScroll: (id)sender
 
1215
{
 
1216
        int new_scroll;
 
1217
        int part=[scroller hitPart];
 
1218
        BOOL update=YES;
 
1219
 
 
1220
        if (part==NSScrollerKnob ||
 
1221
            part==NSScrollerKnobSlot)
 
1222
        {
 
1223
                float f=[scroller floatValue];
 
1224
                new_scroll=(f-1.0)*sb_length;
 
1225
                update=NO;
 
1226
        }
 
1227
        else if (part==NSScrollerDecrementLine)
 
1228
                new_scroll=current_scroll-1;
 
1229
        else if (part==NSScrollerDecrementPage)
 
1230
                new_scroll=current_scroll-sy/2;
 
1231
        else if (part==NSScrollerIncrementLine)
 
1232
                new_scroll=current_scroll+1;
 
1233
        else if (part==NSScrollerIncrementPage)
 
1234
                new_scroll=current_scroll+sy/2;
 
1235
        else
 
1236
                return;
 
1237
 
 
1238
        [self _scrollTo: new_scroll  update: update];
 
1239
}
 
1240
 
 
1241
-(void) setScroller: (NSScroller *)sc
 
1242
{
 
1243
        [scroller setTarget: nil];
 
1244
        ASSIGN(scroller,sc);
 
1245
        [self _updateScroller];
 
1246
        [scroller setTarget: self];
 
1247
        [scroller setAction: @selector(_updateScroll:)];
 
1248
}
 
1249
 
 
1250
@end
 
1251
 
 
1252
 
 
1253
/**
 
1254
Keyboard events
 
1255
**/
 
1256
 
 
1257
@implementation TerminalView (keyboard)
 
1258
 
 
1259
-(void) keyDown: (NSEvent *)e
 
1260
{
 
1261
        NSString *s=[e charactersIgnoringModifiers];
 
1262
 
 
1263
        NSDebugLLog(@"key",@"got key flags=%08x  repeat=%i '%@' '%@' %4i %04x %i %04x %i\n",
 
1264
                [e modifierFlags],[e isARepeat],[e characters],[e charactersIgnoringModifiers],[e keyCode],
 
1265
                [[e characters] characterAtIndex: 0],[[e characters] length],
 
1266
                [[e charactersIgnoringModifiers] characterAtIndex: 0],[[e charactersIgnoringModifiers] length]);
 
1267
 
 
1268
        if ([s length]==1 && ([e modifierFlags]&NSShiftKeyMask))
 
1269
        {
 
1270
                unichar ch=[s characterAtIndex: 0];
 
1271
                if (ch==NSPageUpFunctionKey)
 
1272
                {
 
1273
                        [self _scrollTo: current_scroll-sy+1  update: YES];
 
1274
                        return;
 
1275
                }
 
1276
                if (ch==NSPageDownFunctionKey)
 
1277
                {
 
1278
                        [self _scrollTo: current_scroll+sy-1  update: YES];
 
1279
                        return;
 
1280
                }
 
1281
        }
 
1282
 
 
1283
        /* don't check until we get here so we handle scrollback page-up/down
 
1284
        even when the view's idle */
 
1285
        if (master_fd==-1)
 
1286
                return;
 
1287
 
 
1288
        [tp handleKeyEvent: e];
 
1289
}
 
1290
 
 
1291
-(BOOL) acceptsFirstResponder
 
1292
{
 
1293
        return YES;
 
1294
}
 
1295
-(BOOL) becomeFirstResponder
 
1296
{
 
1297
        return YES;
 
1298
}
 
1299
-(BOOL) resignFirstResponder
 
1300
{
 
1301
        return YES;
 
1302
}
 
1303
 
 
1304
@end
 
1305
 
 
1306
 
 
1307
/**
 
1308
Selection, copy/paste/services
 
1309
**/
 
1310
 
 
1311
@implementation TerminalView (selection)
 
1312
 
 
1313
-(NSString *) _selectionAsString
 
1314
{
 
1315
        int ofs=max_scrollback*sx;
 
1316
        NSMutableString *mstr;
 
1317
        NSString *tmp;
 
1318
        unichar buf[32];
 
1319
        unichar ch;
 
1320
        int len,ws_len;
 
1321
        int i,j;
 
1322
 
 
1323
        if (selection.length==0)
 
1324
                return nil;
 
1325
 
 
1326
        mstr=[[NSMutableString alloc] init];
 
1327
        j=selection.location+selection.length;
 
1328
        len=0;
 
1329
        for (i=selection.location;i<j;i++)
 
1330
        {
 
1331
                ws_len=0;
 
1332
                while (1)
 
1333
                {
 
1334
                        if (i<0)
 
1335
                                ch=sbuf[ofs+i].ch;
 
1336
                        else
 
1337
                                ch=screen[i].ch;
 
1338
 
 
1339
                        if (ch!=' ' && ch!=0 && ch!=MULTI_CELL_GLYPH)
 
1340
                                break;
 
1341
                        ws_len++;
 
1342
                        i++;
 
1343
 
 
1344
                        if (i%sx==0)
 
1345
                        {
 
1346
                                if (i>j)
 
1347
                                {
 
1348
                                        ws_len=0; /* make sure we break out of the outer loop */
 
1349
                                        break;
 
1350
                                }
 
1351
                                if (len)
 
1352
                                {
 
1353
                                        tmp=[[NSString alloc] initWithCharacters: buf length: len];
 
1354
                                        [mstr appendString: tmp];
 
1355
                                        DESTROY(tmp);
 
1356
                                        len=0;
 
1357
                                }
 
1358
                                [mstr appendString: @"\n"];
 
1359
                                ws_len=0;
 
1360
                                continue;
 
1361
                        }
 
1362
                }
 
1363
 
 
1364
                i-=ws_len;
 
1365
 
 
1366
                for (;i<j && ws_len;i++,ws_len--)
 
1367
                {
 
1368
                        buf[len++]=' ';
 
1369
                        if (len==32)
 
1370
                        {
 
1371
                                tmp=[[NSString alloc] initWithCharacters: buf length: 32];
 
1372
                                [mstr appendString: tmp];
 
1373
                                DESTROY(tmp);
 
1374
                                len=0;
 
1375
                        }
 
1376
                }
 
1377
                if (i>=j)
 
1378
                        break;
 
1379
 
 
1380
                buf[len++]=ch;
 
1381
                if (len==32)
 
1382
                {
 
1383
                        tmp=[[NSString alloc] initWithCharacters: buf length: 32];
 
1384
                        [mstr appendString: tmp];
 
1385
                        DESTROY(tmp);
 
1386
                        len=0;
 
1387
                }
 
1388
        }
 
1389
 
 
1390
        if (len)
 
1391
        {
 
1392
                tmp=[[NSString alloc] initWithCharacters: buf length: len];
 
1393
                [mstr appendString: tmp];
 
1394
                DESTROY(tmp);
 
1395
        }
 
1396
 
 
1397
        return AUTORELEASE(mstr);
 
1398
}
 
1399
 
 
1400
 
 
1401
-(void) _setSelection: (struct selection_range)s
 
1402
{
 
1403
        int i,j,ofs2;
 
1404
 
 
1405
        if (s.location<-sb_length*sx)
 
1406
        {
 
1407
                s.length+=sb_length*sx+s.location;
 
1408
                s.location=-sb_length*sx;
 
1409
        }
 
1410
        if (s.location+s.length>sx*sy)
 
1411
        {
 
1412
                s.length=sx*sy-s.location;
 
1413
        }
 
1414
 
 
1415
        if (!s.length && !selection.length)
 
1416
                return;
 
1417
        if (s.length==selection.length && s.location==selection.location)
 
1418
                return;
 
1419
 
 
1420
        ofs2=max_scrollback*sx;
 
1421
 
 
1422
        j=selection.location+selection.length;
 
1423
        if (j>s.location)
 
1424
                j=s.location;
 
1425
 
 
1426
        for (i=selection.location;i<j && i<0;i++)
 
1427
        {
 
1428
                sbuf[ofs2+i].attr&=0xbf;
 
1429
                sbuf[ofs2+i].attr|=0x80;
 
1430
        }
 
1431
        for (;i<j;i++)
 
1432
        {
 
1433
                screen[i].attr&=0xbf;
 
1434
                screen[i].attr|=0x80;
 
1435
        }
 
1436
 
 
1437
        i=s.location+s.length;
 
1438
        if (i<selection.location)
 
1439
                i=selection.location;
 
1440
        j=selection.location+selection.length;
 
1441
        for (;i<j && i<0;i++)
 
1442
        {
 
1443
                sbuf[ofs2+i].attr&=0xbf;
 
1444
                sbuf[ofs2+i].attr|=0x80;
 
1445
        }
 
1446
        for (;i<j;i++)
 
1447
        {
 
1448
                screen[i].attr&=0xbf;
 
1449
                screen[i].attr|=0x80;
 
1450
        }
 
1451
 
 
1452
        i=s.location;
 
1453
        j=s.location+s.length;
 
1454
        for (;i<j && i<0;i++)
 
1455
        {
 
1456
                if (!(sbuf[ofs2+i].attr&0x40))
 
1457
                        sbuf[ofs2+i].attr|=0xc0;
 
1458
        }
 
1459
        for (;i<j;i++)
 
1460
        {
 
1461
                if (!(screen[i].attr&0x40))
 
1462
                        screen[i].attr|=0xc0;
 
1463
        }
 
1464
 
 
1465
        selection=s;
 
1466
        [self setNeedsLazyDisplayInRect: [self bounds]];
 
1467
}
 
1468
 
 
1469
-(void) _clearSelection
 
1470
{
 
1471
        struct selection_range s;
 
1472
        s.location=s.length=0;
 
1473
        [self _setSelection: s];
 
1474
}
 
1475
 
 
1476
 
 
1477
-(void) copy: (id)sender
 
1478
{
 
1479
        NSPasteboard *pb=[NSPasteboard generalPasteboard];
 
1480
        NSString *s=[self _selectionAsString];
 
1481
        if (!s)
 
1482
        {
 
1483
                NSBeep();
 
1484
                return;
 
1485
        }
 
1486
        [pb declareTypes: [NSArray arrayWithObject: NSStringPboardType]
 
1487
                owner: self];
 
1488
        [pb setString: s forType: NSStringPboardType];
 
1489
}
 
1490
 
 
1491
-(void) paste: (id)sender
 
1492
{
 
1493
        NSPasteboard *pb=[NSPasteboard generalPasteboard];
 
1494
        NSString *type;
 
1495
        NSString *str;
 
1496
 
 
1497
        type=[pb availableTypeFromArray: [NSArray arrayWithObject: NSStringPboardType]];
 
1498
        if (!type)
 
1499
                return;
 
1500
        str=[pb stringForType: NSStringPboardType];
 
1501
        if (str)
 
1502
                [tp sendString: str];
 
1503
}
 
1504
 
 
1505
-(BOOL) writeSelectionToPasteboard: (NSPasteboard *)pb
 
1506
        types: (NSArray *)t
 
1507
{
 
1508
        int i;
 
1509
        NSString *s;
 
1510
 
 
1511
        s=[self _selectionAsString];
 
1512
        if (!s)
 
1513
        {
 
1514
                NSBeep();
 
1515
                return NO;
 
1516
        }
 
1517
 
 
1518
        [pb declareTypes: t  owner: self];
 
1519
        for (i=0;i<[t count];i++)
 
1520
        {
 
1521
                if ([[t objectAtIndex: i] isEqual: NSStringPboardType])
 
1522
                {
 
1523
                        [pb setString: s
 
1524
                                forType: NSStringPboardType];
 
1525
                        return YES;
 
1526
                }
 
1527
        }
 
1528
        return NO;
 
1529
}
 
1530
 
 
1531
-(BOOL) readSelectionFromPasteboard: (NSPasteboard *)pb
 
1532
{ /* TODO: is it really necessary to implement this? */
 
1533
        return YES;
 
1534
}
 
1535
 
 
1536
-(id) validRequestorForSendType: (NSString *)st
 
1537
        returnType: (NSString *)rt
 
1538
{
 
1539
        if (!selection.length)
 
1540
                return nil;
 
1541
        if (st!=nil && ![st isEqual: NSStringPboardType])
 
1542
                return nil;
 
1543
        if (rt!=nil)
 
1544
                return nil;
 
1545
        return self;
 
1546
}
 
1547
 
 
1548
 
 
1549
/* Return the range we should select for the given position and granularity:
 
1550
 0   characters
 
1551
 1   words
 
1552
 2   lines
 
1553
*/
 
1554
-(struct selection_range) _selectionRangeAt: (int)pos  granularity: (int)g
 
1555
{
 
1556
        struct selection_range s;
 
1557
 
 
1558
        if (g==3)
 
1559
        { /* select lines */
 
1560
                int l=floor(pos/(float)sx);
 
1561
                s.location=l*sx;
 
1562
                s.length=sx;
 
1563
                return s;
 
1564
        }
 
1565
 
 
1566
        if (g==2)
 
1567
        { /* select words */
 
1568
                int ofs=max_scrollback*sx;
 
1569
                unichar ch,ch2;
 
1570
                NSCharacterSet *cs;
 
1571
                int i,j;
 
1572
 
 
1573
                if (pos<0)
 
1574
                        ch=sbuf[ofs+pos].ch;
 
1575
                else
 
1576
                        ch=screen[pos].ch;
 
1577
                if (ch==0) ch=' ';
 
1578
 
 
1579
                /* try to find a character set for this character */
 
1580
                cs=[NSCharacterSet alphanumericCharacterSet];
 
1581
                if (![cs characterIsMember: ch])
 
1582
                        cs=[NSCharacterSet punctuationCharacterSet];
 
1583
                if (![cs characterIsMember: ch])
 
1584
                        cs=[NSCharacterSet whitespaceCharacterSet];
 
1585
                if (![cs characterIsMember: ch])
 
1586
                {
 
1587
                        s.location=pos;
 
1588
                        s.length=1;
 
1589
                        return s;
 
1590
                }
 
1591
 
 
1592
                /* search the line backwards for a boundary */
 
1593
                j=floor(pos/(float)sx);
 
1594
                j*=sx;
 
1595
                for (i=pos-1;i>=j;i--)
 
1596
                {
 
1597
                        if (i<0)
 
1598
                                ch2=sbuf[ofs+i].ch;
 
1599
                        else
 
1600
                                ch2=screen[i].ch;
 
1601
                        if (ch2==0) ch2=' ';
 
1602
 
 
1603
                        if (![cs characterIsMember: ch2])
 
1604
                                break;
 
1605
                }
 
1606
                s.location=i+1;
 
1607
 
 
1608
                /* and forwards... */
 
1609
                j+=sx;
 
1610
                for (i=pos+1;i<j;i++)
 
1611
                {
 
1612
                        if (i<0)
 
1613
                                ch2=sbuf[ofs+i].ch;
 
1614
                        else
 
1615
                                ch2=screen[i].ch;
 
1616
                        if (ch2==0) ch2=' ';
 
1617
 
 
1618
                        if (![cs characterIsMember: ch2])
 
1619
                                break;
 
1620
                }
 
1621
                s.length=i-s.location;
 
1622
                return s;
 
1623
        }
 
1624
 
 
1625
        s.location=pos;
 
1626
        s.length=0;
 
1627
 
 
1628
        return s;
 
1629
}
 
1630
 
 
1631
-(void) mouseDown: (NSEvent *)e
 
1632
{
 
1633
        int ofs0,ofs1,first;
 
1634
        NSPoint p;
 
1635
        struct selection_range s;
 
1636
        int g;
 
1637
        struct selection_range r0,r1;
 
1638
 
 
1639
        first=YES;
 
1640
        ofs0=0; /* get compiler to shut up */
 
1641
        g=[e clickCount];
 
1642
        while ([e type]!=NSLeftMouseUp)
 
1643
        {
 
1644
                p=[e locationInWindow];
 
1645
 
 
1646
                p=[self convertPoint: p  fromView: nil];
 
1647
                p.x=floor((p.x-border_x)/fx);
 
1648
                if (p.x<0) p.x=0;
 
1649
                if (p.x>=sx) p.x=sx-1;
 
1650
                p.y=ceil((p.y-border_y)/fy);
 
1651
                if (p.y<-1) p.y=-1;
 
1652
                if (p.y>sy) p.y=sy;
 
1653
                p.y=sy-p.y+current_scroll;
 
1654
                ofs1=((int)p.x)+((int)p.y)*sx;
 
1655
 
 
1656
                r1=[self _selectionRangeAt: ofs1  granularity: g];
 
1657
                if (first)
 
1658
                {
 
1659
                        ofs0=ofs1;
 
1660
                        first=0;
 
1661
                        r0=r1;
 
1662
                }
 
1663
 
 
1664
                NSDebugLLog(@"select",@"ofs %i %i (%i+%i) (%i+%i)\n",
 
1665
                        ofs0,ofs1,
 
1666
                        r0.location,r0.length,
 
1667
                        r1.location,r1.length);
 
1668
 
 
1669
                if (ofs1>ofs0)
 
1670
                {
 
1671
                        s.location=r0.location;
 
1672
                        s.length=r1.location+r1.length-r0.location;
 
1673
                }
 
1674
                else
 
1675
                {
 
1676
                        s.location=r1.location;
 
1677
                        s.length=r0.location+r0.length-r1.location;
 
1678
                }
 
1679
 
 
1680
                [self _setSelection: s];
 
1681
                [self displayIfNeeded];
 
1682
 
 
1683
                e=[NSApp nextEventMatchingMask: NSLeftMouseDownMask|NSLeftMouseUpMask|
 
1684
                                                NSLeftMouseDraggedMask|NSMouseMovedMask
 
1685
                        untilDate: [NSDate distantFuture]
 
1686
                        inMode: NSEventTrackingRunLoopMode
 
1687
                        dequeue: YES];
 
1688
        }
 
1689
 
 
1690
        if (selection.length)
 
1691
        {
 
1692
                [self writeSelectionToPasteboard: [NSPasteboard pasteboardWithName: @"Selection"]
 
1693
                        types: [NSArray arrayWithObject: NSStringPboardType]];
 
1694
        }
 
1695
}
 
1696
 
 
1697
-(void) otherMouseUp: (NSEvent *)e
 
1698
{
 
1699
        NSPasteboard *pb=[NSPasteboard pasteboardWithName: @"Selection"];
 
1700
        NSString *type;
 
1701
        NSString *str;
 
1702
 
 
1703
        type=[pb availableTypeFromArray: [NSArray arrayWithObject: NSStringPboardType]];
 
1704
        if (!type)
 
1705
                return;
 
1706
        str=[pb stringForType: NSStringPboardType];
 
1707
        if (str)
 
1708
                [tp sendString: str];
 
1709
}
 
1710
 
 
1711
@end
 
1712
 
 
1713
 
 
1714
/**
 
1715
Handle master_fd
 
1716
**/
 
1717
 
 
1718
@implementation TerminalView (input)
 
1719
 
 
1720
-(NSDate *) timedOutEvent: (void *)data type: (RunLoopEventType)t
 
1721
        forMode: (NSString *)mode
 
1722
{
 
1723
        NSLog(@"timedOutEvent:type:forMode: ignored");
 
1724
        return nil;
 
1725
}
 
1726
 
 
1727
-(void) readData
 
1728
{
 
1729
        char buf[256];
 
1730
        int size,total,i;
 
1731
 
 
1732
//      get_zombies();
 
1733
 
 
1734
        total=0;
 
1735
        num_scrolls=0;
 
1736
        dirty.x0=-1;
 
1737
 
 
1738
        current_x=cursor_x;
 
1739
        current_y=cursor_y;
 
1740
 
 
1741
        [self _clearSelection]; /* TODO? */
 
1742
 
 
1743
        NSDebugLLog(@"term",@"receiving output");
 
1744
 
 
1745
        while (1)
 
1746
        {
 
1747
                size=read(master_fd,buf,sizeof(buf));
 
1748
                if (size<0 && errno==EAGAIN)
 
1749
                        break;
 
1750
                if (size<=0)
 
1751
                {
 
1752
                        NSString *msg;
 
1753
                        int i,c;
 
1754
                        unichar ch;
 
1755
 
 
1756
//                      get_zombies();
 
1757
                        [self closeProgram];
 
1758
 
 
1759
                        msg=_(@"[Process exited]");
 
1760
                        c=[msg length];
 
1761
                        for (i=0;i<c;i++)
 
1762
                        {
 
1763
                                ch=[msg characterAtIndex: i];
 
1764
                                if (ch<256) /* TODO */
 
1765
                                        [tp processByte: ch];
 
1766
                        }
 
1767
                        [tp processByte: '\n'];
 
1768
                        [tp processByte: '\r'];
 
1769
 
 
1770
                        /* Sending this notification might cause us to be deallocated, in
 
1771
                        which case we can't let the rest of code here run (and we'd rather
 
1772
                        not to avoid a pointless update of the screen). To detect this, we
 
1773
                        retain ourself before the call and check the retaincount after. */
 
1774
                        [self retain];
 
1775
                        [[NSNotificationCenter defaultCenter]
 
1776
                                postNotificationName: TerminalViewBecameIdleNotification
 
1777
                                object: self];
 
1778
                        if ([self retainCount]==1)
 
1779
                        { /* we only have our own retain left, so we release ourself
 
1780
                          (causing us to be deallocated) and return */
 
1781
                                [self release];
 
1782
                                return;
 
1783
                        }
 
1784
                        [self release];
 
1785
 
 
1786
                        break;
 
1787
                }
 
1788
 
 
1789
 
 
1790
                for (i=0;i<size;i++)
 
1791
                        [tp processByte: buf[i]];
 
1792
 
 
1793
                total+=size;
 
1794
                /*
 
1795
                Don't get stuck processing input forever; give other terminal windows
 
1796
                and the user a chance to do things. The numbers affect latency versus
 
1797
                throughput. High numbers means more input is processed before the
 
1798
                screen is updated, leading to higher throughput but also to more
 
1799
                'jerky' updates. Low numbers would give smoother updating and less
 
1800
                latency, but throughput goes down.
 
1801
 
 
1802
                TODO: tweak more? seems pretty good now
 
1803
                */
 
1804
                if (total>=8192 || (num_scrolls+abs(pending_scroll))>10)
 
1805
                        break;
 
1806
        }
 
1807
 
 
1808
        if (cursor_x!=current_x || cursor_y!=current_y)
 
1809
        {
 
1810
                ADD_DIRTY(current_x,current_y,1,1);
 
1811
                SCREEN(current_x,current_y).attr|=0x80;
 
1812
                ADD_DIRTY(cursor_x,cursor_y,1,1);
 
1813
                draw_cursor=YES;
 
1814
        }
 
1815
 
 
1816
        NSDebugLLog(@"term",@"done (%i %i) (%i %i)\n",
 
1817
                dirty.x0,dirty.y0,dirty.x1,dirty.y1);
 
1818
 
 
1819
        if (dirty.x0>=0)
 
1820
        {
 
1821
                NSRect dr;
 
1822
 
 
1823
//              NSLog(@"dirty=(%i %i)-(%i %i)\n",dirty.x0,dirty.y0,dirty.x1,dirty.y1);
 
1824
                dr.origin.x=dirty.x0*fx;
 
1825
                dr.origin.y=dirty.y0*fy;
 
1826
                dr.size.width=(dirty.x1-dirty.x0)*fx;
 
1827
                dr.size.height=(dirty.y1-dirty.y0)*fy;
 
1828
                dr.origin.y=fy*sy-(dr.origin.y+dr.size.height);
 
1829
//              NSLog(@"-> dirty=(%g %g)+(%g %g)\n",dirty.origin.x,dirty.origin.y,dirty.size.width,dirty.size.height);
 
1830
                dr.origin.x+=border_x;
 
1831
                dr.origin.y+=border_y;
 
1832
                [self setNeedsLazyDisplayInRect: dr];
 
1833
 
 
1834
                if (current_scroll!=0)
 
1835
                { /* TODO */
 
1836
                        current_scroll=0;
 
1837
                        [self setNeedsDisplay: YES];
 
1838
                }
 
1839
 
 
1840
                [self _updateScroller];
 
1841
        }
 
1842
}
 
1843
 
 
1844
-(void) writePendingData
 
1845
{
 
1846
        int l,new_size;
 
1847
        l=write(master_fd,write_buf,write_buf_len);
 
1848
        if (l<0)
 
1849
        {
 
1850
                if (errno!=EAGAIN)
 
1851
                        NSLog(_(@"Unexpected error while writing: %m."));
 
1852
                return;
 
1853
        }
 
1854
        memmove(write_buf,&write_buf[l],write_buf_len-l);
 
1855
        write_buf_len-=l;
 
1856
 
 
1857
        /* If less than half the buffer is empty, reallocate it, but never free
 
1858
        it completely. */
 
1859
        new_size=(write_buf_len+511)&~511;
 
1860
        if (!new_size)
 
1861
                new_size=512;
 
1862
        if (new_size<=write_buf_size/2)
 
1863
        {
 
1864
                write_buf_size=new_size;
 
1865
                write_buf=realloc(write_buf,write_buf_size);
 
1866
        }
 
1867
 
 
1868
        if (!write_buf_len)
 
1869
        {
 
1870
                [[NSRunLoop currentRunLoop] removeEvent: (void *)master_fd
 
1871
                        type: ET_WDESC
 
1872
                        forMode: NSDefaultRunLoopMode
 
1873
                        all: YES];
 
1874
        }
 
1875
}
 
1876
 
 
1877
-(void) receivedEvent: (void *)data
 
1878
        type: (RunLoopEventType)type
 
1879
        extra: (void *)extra
 
1880
        forMode: (NSString *)mode
 
1881
{
 
1882
        if (type==ET_WDESC)
 
1883
                [self writePendingData];
 
1884
        else if (type==ET_RDESC)
 
1885
                [self readData];
 
1886
}
 
1887
 
 
1888
 
 
1889
-(void) closeProgram
 
1890
{
 
1891
        if (master_fd==-1)
 
1892
                return;
 
1893
        NSDebugLLog(@"pty",@"closing master fd=%i\n",master_fd);
 
1894
        [[NSRunLoop currentRunLoop] removeEvent: (void *)master_fd
 
1895
                type: ET_RDESC
 
1896
                forMode: NSDefaultRunLoopMode
 
1897
                all: YES];
 
1898
        [[NSRunLoop currentRunLoop] removeEvent: (void *)master_fd
 
1899
                type: ET_WDESC
 
1900
                forMode: NSDefaultRunLoopMode
 
1901
                all: YES];
 
1902
        write_buf_len=write_buf_size=0;
 
1903
        free(write_buf);
 
1904
        write_buf=NULL;
 
1905
        close(master_fd);
 
1906
        master_fd=-1;
 
1907
}
 
1908
 
 
1909
 
 
1910
-(void) runProgram: (NSString *)path
 
1911
        withArguments: (NSArray *)args
 
1912
        inDirectory: (NSString *)directory
 
1913
        initialInput: (NSString *)d
 
1914
        arg0: (NSString *)arg0
 
1915
{
 
1916
        int ret;
 
1917
        struct winsize ws;
 
1918
        NSRunLoop *rl;
 
1919
        const char *cpath;
 
1920
        const char *cargs[[args count]+2];
 
1921
        const char *cdirectory;
 
1922
        int i;
 
1923
        int pipefd[2];
 
1924
        int flags;
 
1925
 
 
1926
        NSDebugLLog(@"pty",@"-runProgram: %@ withArguments: %@ initialInput: %@",
 
1927
                path,args,d);
 
1928
 
 
1929
        [self closeProgram];
 
1930
 
 
1931
        cpath=[path cString];
 
1932
        if (arg0)
 
1933
                cargs[0]=[arg0 cString];
 
1934
        else
 
1935
                cargs[0]=cpath;
 
1936
        cdirectory=[directory cString];
 
1937
        for (i=0;i<[args count];i++)
 
1938
        {
 
1939
                cargs[i+1]=[[args objectAtIndex: i] cString];
 
1940
        }
 
1941
        cargs[i+1]=NULL;
 
1942
 
 
1943
        if (d)
 
1944
        {
 
1945
                if (pipe(pipefd))
 
1946
                {
 
1947
                        NSLog(_(@"Unable to open pipe for input: %m."));
 
1948
                        return;
 
1949
                }
 
1950
                NSDebugLLog(@"pty",@"creating pipe for initial data, got %i %i",
 
1951
                        pipefd[0],pipefd[1]);
 
1952
        }
 
1953
 
 
1954
        ws.ws_row=sy;
 
1955
        ws.ws_col=sx;
 
1956
        ret=forkpty(&master_fd,NULL,NULL,&ws);
 
1957
        if (ret<0)
 
1958
        {
 
1959
                NSLog(_(@"Unable to fork: %m."));
 
1960
                return;
 
1961
        }
 
1962
 
 
1963
        if (ret==0)
 
1964
        {
 
1965
                if (d)
 
1966
                {
 
1967
                        close(pipefd[1]);
 
1968
                        dup2(pipefd[0],0);
 
1969
                }
 
1970
 
 
1971
                if (cdirectory)
 
1972
                        chdir(cdirectory);
 
1973
                putenv("TERM=linux");
 
1974
                putenv("TERM_PROGRAM=GNUstep_Terminal");
 
1975
                execv(cpath,(char *const*)cargs);
 
1976
                fprintf(stderr,"Unable to spawn process '%s': %m!",cpath);
 
1977
                exit(1);
 
1978
        }
 
1979
 
 
1980
        NSDebugLLog(@"pty",@"forked child %i, fd %i",ret,master_fd);
 
1981
 
 
1982
        /* Set non-blocking mode for the descriptor. */
 
1983
        flags=fcntl(master_fd,F_GETFL,0);
 
1984
        if (flags==-1)
 
1985
        {
 
1986
                NSLog(_(@"Unable to set non-blocking mode: %m."));
 
1987
        }
 
1988
        else
 
1989
        {
 
1990
                flags|=O_NONBLOCK;
 
1991
                fcntl(master_fd,F_SETFL,flags);
 
1992
        }
 
1993
 
 
1994
        rl=[NSRunLoop currentRunLoop];
 
1995
        [rl addEvent: (void *)master_fd
 
1996
                type: ET_RDESC
 
1997
                watcher: self
 
1998
                forMode: NSDefaultRunLoopMode];
 
1999
 
 
2000
        [[NSNotificationCenter defaultCenter]
 
2001
                postNotificationName: TerminalViewBecameNonIdleNotification
 
2002
                object: self];
 
2003
 
 
2004
        if (d)
 
2005
        {
 
2006
                const char *s=[d UTF8String];
 
2007
                close(pipefd[0]);
 
2008
                write(pipefd[1],s,strlen(s));
 
2009
                close(pipefd[1]);
 
2010
        }
 
2011
 
 
2012
        DESTROY(title_window);
 
2013
        if (args)
 
2014
                title_window=[[NSString stringWithFormat: @"%@ %@",
 
2015
                        path,[args componentsJoinedByString: @" "]] retain];
 
2016
        else
 
2017
                title_window=[path copy];
 
2018
 
 
2019
        ASSIGN(title_miniwindow,path);
 
2020
        [[NSNotificationCenter defaultCenter]
 
2021
                postNotificationName: TerminalViewTitleDidChangeNotification
 
2022
                object: self];
 
2023
}
 
2024
 
 
2025
-(void) runProgram: (NSString *)path
 
2026
        withArguments: (NSArray *)args
 
2027
        initialInput: (NSString *)d
 
2028
{
 
2029
        [self runProgram: path
 
2030
                withArguments: args
 
2031
                inDirectory: nil
 
2032
                initialInput: d
 
2033
                arg0: path];
 
2034
}
 
2035
 
 
2036
-(void) runShell
 
2037
{
 
2038
        NSString *arg0;
 
2039
        NSString *path;
 
2040
 
 
2041
        path=[TerminalViewShellPrefs shell];
 
2042
        if ([TerminalViewShellPrefs loginShell])
 
2043
                arg0=[@"-" stringByAppendingString: path];
 
2044
        else
 
2045
                arg0=path;
 
2046
        [self runProgram: path
 
2047
                withArguments: nil
 
2048
                inDirectory: nil
 
2049
                initialInput: nil
 
2050
                arg0: arg0];
 
2051
}
 
2052
 
 
2053
@end
 
2054
 
 
2055
 
 
2056
/**
 
2057
drag'n'drop support
 
2058
**/
 
2059
 
 
2060
@implementation TerminalView (drag_n_drop)
 
2061
 
 
2062
static int handled_mask=
 
2063
        NSDragOperationCopy|NSDragOperationPrivate|NSDragOperationGeneric;
 
2064
 
 
2065
-(unsigned int) draggingEntered: (id<NSDraggingInfo>)sender
 
2066
{
 
2067
        NSArray *types=[[sender draggingPasteboard] types];
 
2068
        unsigned int mask=[sender draggingSourceOperationMask];
 
2069
 
 
2070
        NSDebugLLog(@"dragndrop",@"TerminalView draggingEntered mask=%x types=%@",mask,types);
 
2071
 
 
2072
        if (mask&handled_mask &&
 
2073
            ([types containsObject: NSFilenamesPboardType] ||
 
2074
             [types containsObject: NSStringPboardType]))
 
2075
                return NSDragOperationCopy;
 
2076
        return 0;
 
2077
}
 
2078
 
 
2079
/* TODO: should I really have to implement this? */
 
2080
-(BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
 
2081
{
 
2082
        NSDebugLLog(@"dragndrop",@"preparing for drag");
 
2083
        return YES;
 
2084
}
 
2085
 
 
2086
-(BOOL) performDragOperation: (id<NSDraggingInfo>)sender
 
2087
{
 
2088
        NSPasteboard *pb=[sender draggingPasteboard];
 
2089
        NSArray *types=[pb types];
 
2090
        unsigned int mask=[sender draggingSourceOperationMask];
 
2091
 
 
2092
        NSDebugLLog(@"dragndrop",@"performDrag %x %@",mask,types);
 
2093
 
 
2094
        if (!(mask&handled_mask))
 
2095
                return NO;
 
2096
 
 
2097
        if ([types containsObject: NSFilenamesPboardType])
 
2098
        {
 
2099
                NSArray *data;
 
2100
                int i,c;
 
2101
 
 
2102
                data=[pb propertyListForType: NSFilenamesPboardType];
 
2103
                if (!data)
 
2104
                        data=[NSUnarchiver unarchiveObjectWithData: [pb dataForType: NSFilenamesPboardType]];
 
2105
 
 
2106
                c=[data count];
 
2107
 
 
2108
                for (i=0;i<c;i++)
 
2109
                {
 
2110
                        [tp sendString: @" "];
 
2111
                        [tp sendString: [data objectAtIndex: i]];
 
2112
                }
 
2113
                return YES;
 
2114
        }
 
2115
 
 
2116
        if ([types containsObject: NSStringPboardType])
 
2117
        {
 
2118
                NSString *str=[pb stringForType: NSStringPboardType];
 
2119
                [tp sendString: str];
 
2120
                return YES;
 
2121
        }
 
2122
 
 
2123
        return NO;
 
2124
}
 
2125
 
 
2126
@end
 
2127
 
 
2128
 
 
2129
/**
 
2130
misc. stuff
 
2131
**/
 
2132
 
 
2133
@implementation TerminalView
 
2134
 
 
2135
-(void) _resizeTerminalTo: (NSSize)size
 
2136
{
 
2137
        int nsx,nsy;
 
2138
        struct winsize ws;
 
2139
        screen_char_t *nscreen,*nsbuf;
 
2140
        int iy,ny;
 
2141
        int copy_sx;
 
2142
 
 
2143
        nsx=(size.width-border_x)/fx;
 
2144
        nsy=(size.height-border_y)/fy;
 
2145
 
 
2146
        NSDebugLLog(@"term",@"_resizeTerminalTo: (%g %g) %i %i (%g %g)\n",
 
2147
                size.width,size.height,
 
2148
                nsx,nsy,
 
2149
                nsx*fx,nsy*fy);
 
2150
 
 
2151
        if (ignore_resize)
 
2152
        {
 
2153
                NSDebugLLog(@"term",@"ignored");
 
2154
                return;
 
2155
        }
 
2156
 
 
2157
        if (nsx<1) nsx=1;
 
2158
        if (nsy<1) nsy=1;
 
2159
 
 
2160
        if (nsx==sx && nsy==sy)
 
2161
        {
 
2162
                /* Do a complete redraw anyway. Even though we don't really need it,
 
2163
                the resize might have caused other things to overwrite our part of the
 
2164
                window. */
 
2165
                draw_all=2;
 
2166
                return;
 
2167
        }
 
2168
 
 
2169
        [self _clearSelection]; /* TODO? */
 
2170
 
 
2171
        nscreen=malloc(nsx*nsy*sizeof(screen_char_t));
 
2172
        nsbuf=malloc(nsx*max_scrollback*sizeof(screen_char_t));
 
2173
        if (!nscreen || !nsbuf)
 
2174
        {
 
2175
                NSLog(@"Failed to allocate screen buffer!");
 
2176
                return;
 
2177
        }
 
2178
        memset(nscreen,0,sizeof(screen_char_t)*nsx*nsy);
 
2179
        memset(nsbuf,0,sizeof(screen_char_t)*nsx*max_scrollback);
 
2180
 
 
2181
        copy_sx=sx;
 
2182
        if (copy_sx>nsx)
 
2183
                copy_sx=nsx;
 
2184
 
 
2185
//      NSLog(@"copy %i+%i %i  (%ix%i)-(%ix%i)\n",start,num,copy_sx,sx,sy,nsx,nsy);
 
2186
 
 
2187
/* TODO: handle resizing and scrollback
 
2188
improve? */
 
2189
        for (iy=-sb_length;iy<sy;iy++)
 
2190
        {
 
2191
                screen_char_t *src,*dst;
 
2192
                ny=iy-sy+nsy;
 
2193
                if (ny<-max_scrollback)
 
2194
                        continue;
 
2195
 
 
2196
                if (iy<0)
 
2197
                        src=&sbuf[sx*(max_scrollback+iy)];
 
2198
                else
 
2199
                        src=&screen[sx*iy];
 
2200
 
 
2201
                if (ny<0)
 
2202
                        dst=&nsbuf[nsx*(max_scrollback+ny)];
 
2203
                else
 
2204
                        dst=&nscreen[nsx*ny];
 
2205
 
 
2206
                memcpy(dst,src,copy_sx*sizeof(screen_char_t));
 
2207
        }
 
2208
 
 
2209
        sb_length=sb_length+sy-nsy;
 
2210
        if (sb_length>max_scrollback)
 
2211
                sb_length=max_scrollback;
 
2212
        if (sb_length<0)
 
2213
                sb_length=0;
 
2214
 
 
2215
        sx=nsx;
 
2216
        sy=nsy;
 
2217
        free(screen);
 
2218
        free(sbuf);
 
2219
        screen=nscreen;
 
2220
        sbuf=nsbuf;
 
2221
 
 
2222
        if (cursor_x>sx) cursor_x=sx-1;
 
2223
        if (cursor_y>sy) cursor_y=sy-1;
 
2224
 
 
2225
        [self _updateScroller];
 
2226
 
 
2227
        [tp setTerminalScreenWidth: sx height: sy];
 
2228
 
 
2229
        if (master_fd!=-1)
 
2230
        {
 
2231
                ws.ws_row=nsy;
 
2232
                ws.ws_col=nsx;
 
2233
                ioctl(master_fd,TIOCSWINSZ,&ws);
 
2234
        }
 
2235
 
 
2236
        [self setNeedsDisplay: YES];
 
2237
}
 
2238
 
 
2239
-(void) setFrame: (NSRect)frame
 
2240
{
 
2241
        [super setFrame: frame];
 
2242
        [self _resizeTerminalTo: frame.size];
 
2243
}
 
2244
 
 
2245
-(void) setFrameSize: (NSSize)size
 
2246
{
 
2247
        [super setFrameSize: size];
 
2248
        [self _resizeTerminalTo: size];
 
2249
}
 
2250
 
 
2251
 
 
2252
- initWithFrame: (NSRect)frame
 
2253
{
 
2254
        sx=80;
 
2255
        sy=25;
 
2256
 
 
2257
        if (!(self=[super initWithFrame: frame])) return nil;
 
2258
 
 
2259
        {
 
2260
                NSSize s;
 
2261
                NSRect r;
 
2262
 
 
2263
                font=[TerminalViewDisplayPrefs terminalFont];
 
2264
                [font retain];
 
2265
 
 
2266
                boldFont=[TerminalViewDisplayPrefs boldTerminalFont];
 
2267
                [boldFont retain];
 
2268
 
 
2269
                r=[font boundingRectForFont];
 
2270
                s=[TerminalView characterCellSize];
 
2271
                fx=s.width;
 
2272
                fy=s.height;
 
2273
 
 
2274
                /* TODO: clear up font metrics issues with xlib/backart */
 
2275
                NSLog(@"NSFont %@ info %@ size %g %@ %d", font, [font fontInfo], [font pointSize], NSStringFromRect([font boundingRectForGlyph: 'A']), [font glyphIsEncoded: 'A']);
 
2276
                fx0=-r.origin.x;
 
2277
                fy0=-r.origin.y;
 
2278
                NSDebugLLog(@"term",@"Bounding (%g %g)+(%g %g)",-fx0,-fy0,fx,fy);
 
2279
                font_encoding=[font mostCompatibleStringEncoding];
 
2280
                boldFont_encoding=[boldFont mostCompatibleStringEncoding];
 
2281
                NSDebugLLog(@"term",@"encoding %i and %i",
 
2282
                        font_encoding,boldFont_encoding);
 
2283
        }
 
2284
 
 
2285
        use_multi_cell_glyphs=[TerminalViewDisplayPrefs useMultiCellGlyphs];
 
2286
 
 
2287
        screen=malloc(sizeof(screen_char_t)*sx*sy);
 
2288
        memset(screen,0,sizeof(screen_char_t)*sx*sy);
 
2289
        draw_all=2;
 
2290
 
 
2291
        max_scrollback=[TerminalViewDisplayPrefs scrollBackLines];
 
2292
        sbuf=malloc(sizeof(screen_char_t)*sx*max_scrollback);
 
2293
        memset(sbuf,0,sizeof(screen_char_t)*sx*max_scrollback);
 
2294
 
 
2295
        tp=[[TerminalParser_Linux alloc] initWithTerminalScreen: self
 
2296
                width: sx  height: sy];
 
2297
 
 
2298
        master_fd=-1;
 
2299
 
 
2300
        [self registerForDraggedTypes: [NSArray arrayWithObjects:
 
2301
                NSFilenamesPboardType,NSStringPboardType,nil]];
 
2302
 
 
2303
        [[NSNotificationCenter defaultCenter]
 
2304
                addObserver: self
 
2305
                selector: @selector(viewPrefsDidChange:)
 
2306
                name: TerminalViewDisplayPrefsDidChangeNotification
 
2307
                object: nil];
 
2308
 
 
2309
        return self;
 
2310
}
 
2311
 
 
2312
-(void) dealloc
 
2313
{
 
2314
        [[NSNotificationCenter defaultCenter]
 
2315
                removeObserver: self];
 
2316
 
 
2317
        [self closeProgram];
 
2318
 
 
2319
        DESTROY(tp);
 
2320
 
 
2321
        [scroller setTarget: nil];
 
2322
        DESTROY(scroller);
 
2323
 
 
2324
        free(screen);
 
2325
        free(sbuf);
 
2326
        screen=NULL;
 
2327
        sbuf=NULL;
 
2328
 
 
2329
        DESTROY(font);
 
2330
        DESTROY(boldFont);
 
2331
 
 
2332
        DESTROY(title_window);
 
2333
        DESTROY(title_miniwindow);
 
2334
 
 
2335
        [super dealloc];
 
2336
}
 
2337
 
 
2338
 
 
2339
-(NSString *) windowTitle
 
2340
{
 
2341
        return title_window;
 
2342
}
 
2343
 
 
2344
-(NSString *) miniwindowTitle
 
2345
{
 
2346
        return title_miniwindow;
 
2347
}
 
2348
 
 
2349
 
 
2350
-(void) setIgnoreResize: (BOOL)ignore
 
2351
{
 
2352
        ignore_resize=ignore;
 
2353
}
 
2354
 
 
2355
-(void) setBorder: (float)x : (float)y
 
2356
{
 
2357
        border_x=x;
 
2358
        border_y=y;
 
2359
}
 
2360
 
 
2361
 
 
2362
+(NSSize) characterCellSize
 
2363
{
 
2364
        NSFont *f=[TerminalViewDisplayPrefs terminalFont];
 
2365
        NSSize s;
 
2366
        s=[f boundingRectForFont].size;
 
2367
        if ([TerminalViewDisplayPrefs useMultiCellGlyphs])
 
2368
        {
 
2369
                s.width=[f boundingRectForGlyph: 'A'].size.width;
 
2370
        }
 
2371
        return s;
 
2372
}
 
2373
 
 
2374
+(void) registerPasteboardTypes
 
2375
{
 
2376
        NSArray *types=[NSArray arrayWithObject: NSStringPboardType];
 
2377
        [NSApp registerServicesMenuSendTypes: types returnTypes: nil];
 
2378
}
 
2379
 
 
2380
@end
 
2381