root/vic/trunk/render/vw.cpp @ 922

Revision 922, 14.0 KB (checked in by piers, 15 years ago)

Alteerd to make work with X and windwos

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1/*
2 * Copyright (c) 1993-1994 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *      This product includes software developed by the University of
16 *      California, Berkeley and the Network Research Group at
17 *      Lawrence Berkeley Laboratory.
18 * 4. Neither the name of the University nor of the Laboratory may be used
19 *    to endorse or promote products derived from this software without
20 *    specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34static const char rcsid[] =
35    "@(#) $Header$ (LBL)";
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include "vw.h"
42#include "color.h"
43#include "rgb-converter.h"
44
45extern "C" {
46#include <tk.h>
47
48#ifdef USE_SHM
49#include <sys/ipc.h>
50#include <sys/shm.h>
51#if defined(sun) && !defined(__svr4__)
52int shmget(key_t, int, int);
53char *shmat(int, char*, int);
54int shmdt(char*);
55int shmctl(int, int, struct shmid_ds*);
56#endif
57#ifdef __osf__
58int XShmGetEventBase(struct _XDisplay *);
59#else
60int XShmGetEventBase(Display *);
61#endif
62#ifdef sgi
63#define XShmAttach __XShmAttach__
64#define XShmDetach __XShmDetach__
65#define XShmPutImage __XShmPutImage__
66#endif
67#include <X11/extensions/XShm.h>
68#ifdef sgi
69#undef XShmAttach
70#undef XShmDetach
71#undef XShmPutImage
72int XShmAttach(Display*, XShmSegmentInfo*);
73int XShmDetach(Display*, XShmSegmentInfo*);
74int XShmPutImage(Display*, Drawable, GC, XImage*, int, int, int, int,
75                 int, int, int);
76#endif
77#endif
78}
79
80static class VideoCommand : public TclObject {
81public:
82        VideoCommand(const char* name) : TclObject(name) { }
83protected:
84        int command(int argc, const char*const* argv);
85} video_cmd("video");
86
87/*
88 * video $path $width $height
89 */
90int VideoCommand::command(int argc, const char*const* argv)
91{
92        Tcl& tcl = Tcl::instance();
93        if (argc != 4) {
94                tcl.result("video arg mismatch");
95                return (TCL_ERROR);
96        }
97        const char* name = argv[1];
98        int width = atoi(argv[2]);
99        int height = atoi(argv[3]);
100        VideoWindow* p = new VideoWindow(name);
101        p->setsize(width, height);
102        tcl.result(p->name());
103
104        return (TCL_OK);
105}
106
107class SlowVideoImage : public StandardVideoImage {
108public:
109        SlowVideoImage(Tk_Window, int width, int height);
110        ~SlowVideoImage();
111        void putimage(Display* dpy, Window window, GC gc,
112                      int sx, int sy, int x, int y,
113                      int w, int h) const;
114};
115
116VideoImage::VideoImage(Tk_Window tk, int w, int h)
117        : width_(w), height_(h)
118{
119        dpy_ = Tk_Display(tk);
120        int depth = Tk_Depth(tk);
121        /*XXX*/
122        bpp_ =  (depth == 24) ? 32 : depth;
123}
124
125VideoImage::~VideoImage()
126{
127}
128
129StandardVideoImage* StandardVideoImage::allocate(Tk_Window tk, int w, int h)
130{
131#ifdef USE_SHM
132        extern int use_shm;
133        if (use_shm) {
134                SharedVideoImage* p = new SharedVideoImage(tk, w, h);
135                if (p->valid())
136                        return (p);
137                delete p;
138        }
139#endif
140        return (new SlowVideoImage(tk, w, h));
141}
142
143StandardVideoImage::StandardVideoImage(Tk_Window tk, int w, int h)
144        : VideoImage(tk, w, h)
145{
146        image_ = XCreateImage(dpy_, Tk_Visual(tk), Tk_Depth(tk),
147                              ZPixmap, 0, (char*)0, w, h, 8, 0);
148}
149
150StandardVideoImage::~StandardVideoImage()
151{
152        /*XXX*/
153#ifndef WIN32
154        XSync(dpy_, 0);
155#endif
156        image_->data = 0;
157        image_->obdata = 0;
158        XDestroyImage(image_);
159}
160
161SlowVideoImage::SlowVideoImage(Tk_Window tk, int w, int h)
162        : StandardVideoImage(tk, w, h)
163{
164        int size = w * h;
165        if (bpp_ > 8)
166                size *= bpp_ / 8;
167        image_->data = new char[size];
168}
169
170SlowVideoImage::~SlowVideoImage()
171{
172        delete image_->data;
173}
174
175void SlowVideoImage::putimage(Display* dpy, Window window, GC gc,
176                              int sx, int sy, int x, int y,
177                              int w, int h) const
178{
179        XPutImage(dpy, window, gc, image_, sx, sy, x, y, w, h);
180}
181
182#ifdef USE_SHM
183SharedVideoImage::SharedVideoImage(Tk_Window tk, int w, int h)
184        : StandardVideoImage(tk, w, h)
185{
186        int size = w * h;
187        if (bpp_ > 8)
188                size *= bpp_ / 8;
189
190        shminfo_.shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0777);
191        if (shminfo_.shmid < 0) {
192                perror("vic: shmget");
193                fprintf(stderr, "\
194vic: reverting to non-shared memory; you should reconfigure your system\n\
195vic: with more a higher limit on shared memory segments.\n\
196vic: refer to the README that accompanies the vic distribution\n");
197                extern int use_shm;
198                use_shm = 0;
199                return;
200        }
201        shminfo_.shmaddr = (char*)shmat(shminfo_.shmid, 0, 0);
202        if (shminfo_.shmaddr == (char*)-1) {
203                perror("shmat");
204                exit(1);
205        }
206        init(tk);
207}
208
209/*
210 * side affect - shmid is detached from local addr space
211 */
212SharedVideoImage::SharedVideoImage(Tk_Window tk, int w, int h,
213                                   u_char* shmaddr, int shmid)
214        : StandardVideoImage(tk, w, h)
215{
216        shminfo_.shmid = shmid;
217        shminfo_.shmaddr = (char*)shmaddr;
218        init(tk);
219}
220
221SharedVideoImage::~SharedVideoImage()
222{
223        if (valid()) {
224                XShmDetach(dpy_, &shminfo_);
225                if (shmdt(shminfo_.shmaddr) < 0)
226                        perror("vic: shmdt");
227        }
228}
229
230void SharedVideoImage::init(Tk_Window tk)
231{
232/*XXX capture-windows need to be writeable */
233#ifdef notdef
234        shminfo_.readOnly = 1;
235#else
236        shminfo_.readOnly = 0;
237#endif
238        XShmAttach(dpy_, &shminfo_);
239        /*
240         * Once the X server has attached the shm segments,
241         * we rmid them so they will go away when we exit.
242         * The sync is to make the X server do the attach
243         * before we do the rmid.
244         */
245        XSync(dpy_, 0);
246        (void)shmctl(shminfo_.shmid, IPC_RMID, 0);
247
248        /*
249         * Wrap segment in an ximage
250         */
251        image_->obdata = (char*)&shminfo_;
252        image_->data = shminfo_.shmaddr;
253}
254
255void SharedVideoImage::putimage(Display* dpy, Window window, GC gc,
256                                int sx, int sy, int x, int y,
257                                int w, int h) const
258{
259        XShmPutImage(dpy, window, gc, image_, sx, sy, x, y, w, h, 0);
260}
261#endif
262
263BareWindow::BareWindow(const char* name, XVisualInfo* vinfo)
264        : TclObject(name), width_(0), height_(0)
265{
266        Tcl& tcl = Tcl::instance();
267        tk_ = Tk_CreateWindowFromPath(tcl.interp(), tcl.tkmain(),
268                                      (char*)name, 0);
269        if (tk_ == 0)
270                abort();
271        Tk_SetClass(tk_, "Vic");
272        Tk_CreateEventHandler(tk_, ExposureMask|StructureNotifyMask,
273                              handle, (ClientData)this);
274        dpy_ = Tk_Display(tk_);
275        if (vinfo != 0) {
276                /*XXX*/
277                Colormap cm = XCreateColormap(dpy_, Tk_WindowId(tcl.tkmain()),
278                                              vinfo->visual, AllocNone);
279                Tk_SetWindowVisual(tk_, vinfo->visual, vinfo->depth, cm);
280        }
281}
282
283BareWindow::~BareWindow()
284{
285        Tk_DeleteEventHandler(tk_, ExposureMask|StructureNotifyMask,
286                              handle, (ClientData)this);
287}
288
289int BareWindow::command(int argc, const char*const* argv)
290{
291        Tcl& tcl = Tcl::instance();
292        if (argc == 2) {
293                if (strcmp(argv[1], "width") == 0) {
294                        sprintf(tcl.buffer(), "%d", width_);
295                        tcl.result(tcl.buffer());
296                        return (TCL_OK);
297                }
298                if (strcmp(argv[1], "height") == 0) {
299                        sprintf(tcl.buffer(), "%d", height_);
300                        tcl.result(tcl.buffer());
301                        return (TCL_OK);
302                }
303        } else if (argc == 4) {
304                if (strcmp(argv[1], "resize") == 0) {
305                        setsize(atoi(argv[2]), atoi(argv[3]));
306                        return (TCL_OK);
307                }
308        }
309        return (TclObject::command(argc, argv));
310}
311
312void BareWindow::handle(ClientData cd, XEvent* ep)
313{
314        BareWindow* w = (BareWindow*)cd;
315       
316        switch (ep->type) {
317        case Expose:
318                if (ep->xexpose.count == 0)
319                        w->redraw();
320                break;
321
322        case DestroyNotify:
323                w->destroy();
324                break;
325
326#ifdef notyet
327        case ConfigureNotify:
328                if (w->width_ != ep->xconfigure.width ||
329                    w->height_ != ep->xconfigure.height)
330                        ;
331                break;
332#endif
333        }
334}
335
336void BareWindow::destroy()
337{
338}
339
340void BareWindow::setsize(int w, int h)
341{
342        width_ = w;
343        height_ = h;
344        Tk_GeometryRequest(tk_, w, h);
345}
346
347GC VideoWindow::gc_;
348
349VideoWindow::VideoWindow(const char* name, XVisualInfo* vinfo)
350        : BareWindow(name, vinfo),
351          vi_(0),
352          callback_pending_(0),
353          damage_(0),
354          voff_(0),
355          hoff_(0)
356{
357        if (gc_ == 0) {
358                /*XXX should use Vic.background */
359                XColor* c = Tk_GetColor(Tcl::instance().interp(), tk_,
360                                        Tk_GetUid("gray50"));
361                if (c == 0)
362                        abort();
363                XGCValues v;
364                v.background = c->pixel;
365                v.foreground = c->pixel;
366                const u_long mask = GCForeground|GCBackground;
367                gc_ = Tk_GetGC(tk_, mask, &v);
368        }
369}
370
371VideoWindow::~VideoWindow()
372{
373        if (callback_pending_)
374                Tk_CancelIdleCall(display, (ClientData)this);
375}
376
377int VideoWindow::command(int argc, const char*const* argv)
378{
379        if (argc == 2) {
380                if (strcmp(argv[1], "redraw") == 0) {
381                        redraw();
382                        return (TCL_OK);
383                }
384                if (strcmp(argv[1], "clear") == 0) {
385                        clear();
386                        return (TCL_OK);
387                }
388                if (strcmp(argv[1], "dim") == 0) {
389                        dim();
390                        return (TCL_OK);
391                }
392        } else if (argc == 3) {
393                if (strcmp(argv[1], "voff") == 0) {
394                        voff_ = atoi(argv[2]);
395                        return (TCL_OK);
396                }
397                if (strcmp(argv[1], "hoff") == 0) {
398                        hoff_ = atoi(argv[2]);
399                        return (TCL_OK);
400                }
401        }
402        return (BareWindow::command(argc, argv));
403}
404
405void VideoWindow::display(ClientData cd)
406{
407        VideoWindow* vw = (VideoWindow*)cd;
408        vw->callback_pending_ = 0;
409        int h = (vw->vi_ != 0) ? vw->vi_->height() : vw->height_;
410        vw->draw(0, h, 0, 0);
411}
412
413void VideoWindow::draw(int y0, int y1, int x0, int x1)
414{
415        if (callback_pending_) {
416                callback_pending_ = 0;
417                Tk_CancelIdleCall(display, (ClientData)this);
418        }
419        if (!Tk_IsMapped(tk_))
420                return;
421
422        Window window = Tk_WindowId(tk_);
423
424        if (vi_ == 0) {
425                XFillRectangle(dpy_, window, gc_, 0, 0, width_, height_);
426                return;
427        }
428        int hoff = (width_ - vi_->width()) >> 1;
429        int voff = (height_ - vi_->height()) >> 1;
430        hoff += hoff_;
431        voff += voff_;
432        /*XXX*/
433        if (damage_) {
434                damage_ = 0;
435                if (hoff > 0) {
436                        XFillRectangle(dpy_, window, gc_,
437                                       0, 0, hoff, height_ + 1);
438                        XFillRectangle(dpy_, window, gc_,
439                                       width_ - hoff, 0, hoff, height_ + 1);
440                }
441                if (voff > 0) {
442                        XFillRectangle(dpy_, window, gc_,
443                                       0, 0, width_, voff + 1);
444                        XFillRectangle(dpy_, window, gc_,
445                                       0, height_ - voff, width_, voff + 1);
446                }
447        }
448        int h = y1 - y0;
449        if (h == 0)
450                h = vi_->height();
451        else if (h > vi_->height())
452                h = vi_->height();
453        else if (h < 0)
454                return;
455        int w = x1 - x0;
456        if (w == 0)
457                w = vi_->width();
458        else if (w > vi_->width())
459                w = vi_->width();
460        else if (w < 0)
461                return;
462
463        vi_->putimage(dpy_, window, gc_, x0, y0, x0 + hoff, y0 + voff, w, h);
464}
465
466/*XXX*/
467void VideoWindow::redraw()
468{
469        damage_ = 1;
470        if (!callback_pending_) {
471                callback_pending_ = 1;
472                Tk_DoWhenIdle(display, (ClientData)this);
473        }
474}
475
476void VideoWindow::clear()
477{
478        if (!callback_pending_) {
479                callback_pending_ = 1;
480                Tk_DoWhenIdle(doclear, (ClientData)this);
481        }
482}
483
484int VideoWindow::bpp()
485{
486    // XXX
487    StandardVideoImage* vi = StandardVideoImage::allocate(tk_, 1, 1);
488    int bpp = vi->ximage()->bits_per_pixel;
489    delete vi;
490    return bpp;
491}
492
493void VideoWindow::dim()
494{
495        if (!callback_pending_) {
496                callback_pending_ = 1;
497                Tk_DoWhenIdle(dodim, (ClientData)this);
498        }
499}
500
501void VideoWindow::doclear(ClientData cd)
502{
503        VideoWindow* vw = (VideoWindow*)cd;
504        vw->callback_pending_ = 0;
505        if (Tk_IsMapped(vw->tk_)) {
506                Window window = Tk_WindowId(vw->tk_);
507                XFillRectangle(vw->dpy_, window, vw->gc_,
508                               0, 0, vw->width_, vw->height_);
509        }
510}
511
512void VideoWindow::dodim(ClientData cd)
513{
514        static Pixmap graypm; /*XXX*/
515        static GC graygc; /*XXX*/
516
517        VideoWindow* vw = (VideoWindow*)cd;
518        vw->callback_pending_ = 0;
519        if (Tk_IsMapped(vw->tk_)) {
520                Window window = Tk_WindowId(vw->tk_);
521                if (graypm == 0) {
522                        u_int32_t bm[32];
523                        for (int i = 0; i < 32; i += 2) {
524                                bm[i]   = 0x55555555;
525                                bm[i+1] = 0xaaaaaaaa;
526                        }
527                        graypm = XCreateBitmapFromData(vw->dpy_, window,
528                                                       (const char*)bm,
529                                                       32, 32);
530                        XColor* c = Tk_GetColor(Tcl::instance().interp(),
531                                                vw->tk_, Tk_GetUid("gray50"));
532                        if (c == 0)
533                                abort();
534                        XGCValues v;
535                        v.background = c->pixel;
536                        v.foreground = c->pixel;
537                        v.fill_style = FillStippled;
538                        v.stipple = graypm;
539                        graygc = Tk_GetGC(vw->tk_,
540                               GCForeground|GCBackground|GCFillStyle|GCStipple,
541                               &v);
542                }
543                XFillRectangle(vw->dpy_, window, graygc,
544                               0, 0, vw->width_, vw->height_);
545        }
546}
547
548void VideoWindow::render(const VideoImage* v, int miny, int maxy,
549                         int minx, int maxx)
550{
551        vi_ = v;
552        draw(miny, maxy, minx, maxx);
553}
554
555CaptureWindow::CaptureWindow(const char* name, XVisualInfo* vinfo)
556        : BareWindow(name, vinfo),
557          base_width_(0),
558          base_height_(0),
559          image_(0)
560{
561        gc_ = Tk_GetGC(tk_, 0, 0);
562}
563
564CaptureWindow::~CaptureWindow()
565{
566        Tk_FreeGC(dpy_, gc_);
567        delete image_;
568}
569
570void CaptureWindow::setsize(int w, int h)
571{
572        BareWindow::setsize(w, h);
573        delete image_;
574        image_ = StandardVideoImage::allocate(tk_, width_, height_);
575}
576
577void CaptureWindow::grab_image()
578{
579        XImage* image = image_->ximage();
580#ifdef USE_SHM
581        if (image->obdata != 0)
582                XShmGetImage(dpy_, Tk_WindowId(tk_), image,
583                             0, 0, AllPlanes);
584        else
585#endif
586                XGetSubImage(Tk_Display(tk_), Tk_WindowId(tk_),
587                             0, 0, image->width, image->height,
588                             AllPlanes, ZPixmap, image, 0, 0);
589}
590                       
591void CaptureWindow::capture(u_int8_t* frm)
592{
593        /*
594         * Xv requires that the window be unobscured in order
595         * to capture the video.  So we grab the server and
596         * raise the window.  This won't work if the window
597         * isn't mapped.  Also, we block signals while
598         * the server is grabbed.  Otherwise, the process
599         * could be stopped while the display is locked out.
600         */
601        if (Tk_IsMapped(tk_)) {
602                raise();
603                grab_image();
604                converter_->convert((u_int8_t*)image_->pixbuf(),
605                                    width_, height_, frm);
606        }
607}
Note: See TracBrowser for help on using the browser.