BRL-CAD
if_ogl.c
Go to the documentation of this file.
1 /* I F _ O G L . C
2  * BRL-CAD
3  *
4  * Copyright (c) 2004-2014 United States Government as represented by
5  * the U.S. Army Research Laboratory.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public License
9  * version 2.1 as published by the Free Software Foundation.
10  *
11  * This library is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this file; see the file named COPYING for more
18  * information.
19  */
20 /** @addtogroup if */
21 /** @{ */
22 /** @file if_ogl.c
23  *
24  * Frame Buffer Library interface for OpenGL.
25  *
26  * There are several different Frame Buffer modes supported. Set your
27  * environment FB_FILE to the appropriate type.
28  *
29  * (see the modeflag definitions below). /dev/ogl[options]
30  *
31  * This code is basically a port of the 4d Framebuffer interface from
32  * IRIS GL to OpenGL.
33  *
34  */
35 /** @} */
36 
37 #include "common.h"
38 
39 #ifdef IF_OGL
40 
41 #ifdef HAVE_SYS_TYPES_H
42 # include <sys/types.h>
43 #endif
44 #include <sys/ipc.h>
45 #include <sys/shm.h>
46 
47 #include <string.h>
48 #include <stdlib.h>
49 #include <ctype.h>
50 #include <signal.h>
51 #include <errno.h>
52 
53 /* glx.h on Mac OS X (and perhaps elsewhere) defines a slew of
54  * parameter names that shadow system symbols. protect the system
55  * symbols by redefining the parameters prior to header inclusion.
56  */
57 #define j1 J1
58 #define y1 Y1
59 #define read rd
60 #define index idx
61 #define access acs
62 #define remainder rem
63 #ifdef HAVE_GL_GLX_H
64 # define class REDEFINE_CLASS_STRING_TO_AVOID_CXX_CONFLICT
65 # include <GL/glx.h>
66 #endif
67 #undef remainder
68 #undef access
69 #undef index
70 #undef read
71 #undef y1
72 #undef j1
73 #ifdef HAVE_GL_GL_H
74 # include <GL/gl.h>
75 #endif
76 
77 #ifdef HAVE_SYS_WAIT_H
78 # include <sys/wait.h>
79 #endif
80 
81 #include "bu/color.h"
82 #include "bu/malloc.h"
83 #include "bu/parallel.h"
84 #include "bu/str.h"
85 #include "fb_private.h"
86 #include "fb.h"
87 #include "fb/fb_ogl.h"
88 
89 #define CJDEBUG 0
90 #define DIRECT_COLOR_VISUAL_ALLOWED 0
91 
92 /* XXX - arbitrary upper bound */
93 #define XMAXSCREEN 16383
94 #define YMAXSCREEN 16383
95 
96 HIDDEN int ogl_nwindows = 0; /* number of open windows */
97 HIDDEN XColor color_cell[256]; /* used to set colormap */
98 
99 
100 /*
101  * Structure of color map in shared memory region. Has exactly the
102  * same format as the SGI hardware "gammaramp" map Note that only the
103  * lower 8 bits are significant.
104  */
105 struct ogl_cmap {
106  short cmr[256];
107  short cmg[256];
108  short cmb[256];
109 };
110 
111 
112 /*
113  * This defines the format of the in-memory framebuffer copy. The
114  * alpha component and reverse order are maintained for compatibility
115  * with /dev/sgi
116  */
117 struct ogl_pixel {
118  unsigned char blue;
119  unsigned char green;
120  unsigned char red;
121  unsigned char alpha;
122 };
123 
124 
125 /* Clipping structure for zoom/pan operations */
126 struct ogl_clip {
127  int xpixmin; /* view clipping planes clipped to pixel memory space*/
128  int xpixmax;
129  int ypixmin;
130  int ypixmax;
131  int xscrmin; /* view clipping planes */
132  int xscrmax;
133  int yscrmin;
134  int yscrmax;
135  double oleft; /* glOrtho parameters */
136  double oright;
137  double otop;
138  double obottom;
139 
140 };
141 
142 
143 /*
144  * Per window state information, overflow area.
145  */
146 struct sgiinfo {
147  short mi_curs_on;
148  short mi_cmap_flag; /* enabled when there is a non-linear map in memory */
149  int mi_shmid;
150  int mi_memwidth; /* width of scanline in if_mem */
151  short mi_xoff; /* X viewport offset, rel. window*/
152  short mi_yoff; /* Y viewport offset, rel. window*/
153  int mi_pid; /* for multi-cpu check */
154  int mi_parent; /* PID of linger-mode process */
155  int mi_doublebuffer; /* 0=singlebuffer 1=doublebuffer */
156  struct ogl_pixel mi_scanline[XMAXSCREEN+1]; /* one scanline */
157 };
158 
159 
160 /*
161  * Per window state information particular to the OpenGL interface
162  */
163 struct oglinfo {
164  GLXContext glxc;
165  Display *dispp; /* pointer to X display connection */
166  Window wind; /* Window identifier */
167  int firstTime;
168  int alive;
169  long event_mask; /* event types to be received */
170  short front_flag; /* front buffer being used (b-mode) */
171  short copy_flag; /* pan and zoom copied from backbuffer */
172  short soft_cmap_flag; /* use software colormapping */
173  int cmap_size; /* hardware colormap size */
174  int win_width; /* actual window width */
175  int win_height; /* actual window height */
176  int vp_width; /* actual viewport width */
177  int vp_height; /* actual viewport height */
178  struct ogl_clip clip; /* current view clipping */
179  Window cursor;
180  XVisualInfo *vip; /* pointer to info on current visual */
181  Colormap xcmap; /* xstyle color map */
182  int use_ext_ctrl; /* for controlling the Ogl graphics engine externally */
183 };
184 
185 
186 #define SGI(ptr) ((struct sgiinfo *)((ptr)->u1.p))
187 #define SGIL(ptr) ((ptr)->u1.p) /* left hand side version */
188 #define OGL(ptr) ((struct oglinfo *)((ptr)->u6.p))
189 #define OGLL(ptr) ((ptr)->u6.p) /* left hand side version */
190 #define if_mem u2.p /* shared memory pointer */
191 #define if_cmap u3.p /* color map in shared memory */
192 #define CMR(x) ((struct ogl_cmap *)((x)->if_cmap))->cmr
193 #define CMG(x) ((struct ogl_cmap *)((x)->if_cmap))->cmg
194 #define CMB(x) ((struct ogl_cmap *)((x)->if_cmap))->cmb
195 #define if_zoomflag u4.l /* zoom > 1 */
196 #define if_mode u5.l /* see MODE_* defines */
197 
198 #define MARGIN 4 /* # pixels margin to screen edge */
199 
200 #define CLIP_XTRA 1
201 
202 #define WIN_L (ifp->if_max_width - ifp->if_width - MARGIN)
203 #define WIN_T (ifp->if_max_height - ifp->if_height - MARGIN)
204 
205 /*
206  * The mode has several independent bits:
207  *
208  * SHARED -vs- MALLOC'ed memory for the image
209  * TRANSIENT -vs- LINGERING windows
210  * Windowed -vs- Centered Full screen
211  * Suppress dither -vs- dither
212  * Double -vs- Single buffered
213  * DrawPixels -vs- CopyPixels
214  */
215 #define MODE_1MASK (1<<0)
216 #define MODE_1SHARED (0<<0) /* Use Shared memory */
217 #define MODE_1MALLOC (1<<0) /* Use malloc memory */
218 
219 #define MODE_2MASK (1<<1)
220 #define MODE_2TRANSIENT (0<<1)
221 #define MODE_2LINGERING (1<<1) /* leave window up after closing*/
222 
223 #define MODE_4MASK (1<<3)
224 #define MODE_4NORMAL (0<<3) /* dither if it seems necessary */
225 #define MODE_4NODITH (1<<3) /* suppress any dithering */
226 
227 #define MODE_7MASK (1<<6)
228 #define MODE_7NORMAL (0<<6) /* install colormap in hardware if possible*/
229 #define MODE_7SWCMAP (1<<6) /* use software colormapping */
230 
231 #define MODE_9MASK (1<<8)
232 #define MODE_9NORMAL (0<<8) /* doublebuffer if possible */
233 #define MODE_9SINGLEBUF (1<<8) /* singlebuffer only */
234 
235 #define MODE_11MASK (1<<10)
236 #define MODE_11NORMAL (0<<10) /* always draw from mem. to window*/
237 #define MODE_11COPY (1<<10) /* keep full image on back buffer */
238 
239 #define MODE_12MASK (1<<11)
240 #define MODE_12NORMAL (0<<11)
241 #define MODE_12DELAY_WRITES_TILL_FLUSH (1<<11)
242 /* and copy current view to front */
243 #define MODE_15MASK (1<<14)
244 #define MODE_15NORMAL (0<<14)
245 #define MODE_15ZAP (1<<14) /* zap the shared memory segment */
246 
247 HIDDEN struct modeflags {
248  char c;
249  long mask;
250  long value;
251  char *help;
252 } modeflags[] = {
253  { 'p', MODE_1MASK, MODE_1MALLOC,
254  "Private memory - else shared" },
255  { 'l', MODE_2MASK, MODE_2LINGERING,
256  "Lingering window" },
257  { 't', MODE_2MASK, MODE_2TRANSIENT,
258  "Transient window" },
259  { 'd', MODE_4MASK, MODE_4NODITH,
260  "Suppress dithering - else dither if not 24-bit buffer" },
261  { 'c', MODE_7MASK, MODE_7SWCMAP,
262  "Perform software colormap - else use hardware colormap if possible" },
263  { 's', MODE_9MASK, MODE_9SINGLEBUF,
264  "Single buffer - else double buffer if possible" },
265  { 'b', MODE_11MASK, MODE_11COPY,
266  "Fast pan and zoom using backbuffer copy - else normal " },
267  { 'D', MODE_12DELAY_WRITES_TILL_FLUSH, MODE_12DELAY_WRITES_TILL_FLUSH,
268  "Don't update screen until fb_flush() is called. (Double buffer sim)" },
269  { 'z', MODE_15MASK, MODE_15ZAP,
270  "Zap (free) shared memory. Can also be done with fbfree command" },
271  { '\0', 0, 0, "" }
272 };
273 
274 
275 HIDDEN void
276 sigkid(int UNUSED(pid))
277 {
278  exit(0);
279 }
280 
281 
282 /* BACKBUFFER_TO_SCREEN - copy pixels from copy on the backbuffer to
283  * the front buffer. Do one scanline specified by one_y, or whole
284  * screen if one_y equals -1.
285  */
286 HIDDEN void
287 backbuffer_to_screen(register fb *ifp, int one_y)
288 {
289  struct ogl_clip *clp;
290 
291  if (!(OGL(ifp)->front_flag)) {
292  OGL(ifp)->front_flag = 1;
293  glDrawBuffer(GL_FRONT);
294  glMatrixMode(GL_PROJECTION);
295  glPopMatrix();
296  glPixelZoom((float) ifp->if_xzoom, (float) ifp->if_yzoom);
297  }
298 
299  clp = &(OGL(ifp)->clip);
300 
301  if (one_y > clp->ypixmax) {
302  return;
303  } else if (one_y < 0) {
304  /* do whole visible screen */
305 
306  /* Blank out area left of image */
307  glColor3b(0, 0, 0);
308  if (clp->xscrmin < 0) glRecti(clp->xscrmin - CLIP_XTRA,
309  clp->yscrmin - CLIP_XTRA,
310  CLIP_XTRA,
311  clp->yscrmax + CLIP_XTRA);
312 
313  /* Blank out area below image */
314  if (clp->yscrmin < 0) glRecti(clp->xscrmin - CLIP_XTRA,
315  clp->yscrmin - CLIP_XTRA,
316  clp->xscrmax + CLIP_XTRA,
317  CLIP_XTRA);
318 
319  /* We are in copy mode, so we use vp_width rather
320  * than if_width
321  */
322  /* Blank out area right of image */
323  if (clp->xscrmax >= OGL(ifp)->vp_width) glRecti(ifp->if_width - CLIP_XTRA,
324  clp->yscrmin - CLIP_XTRA,
325  clp->xscrmax + CLIP_XTRA,
326  clp->yscrmax + CLIP_XTRA);
327 
328  /* Blank out area above image */
329  if (clp->yscrmax >= OGL(ifp)->vp_height) glRecti(clp->xscrmin - CLIP_XTRA,
330  OGL(ifp)->vp_height - CLIP_XTRA,
331  clp->xscrmax + CLIP_XTRA,
332  clp->yscrmax + CLIP_XTRA);
333 
334  /* copy image from backbuffer */
335  glRasterPos2i(clp->xpixmin, clp->ypixmin);
336  glCopyPixels(SGI(ifp)->mi_xoff + clp->xpixmin,
337  SGI(ifp)->mi_yoff + clp->ypixmin,
338  clp->xpixmax - clp->xpixmin +1,
339  clp->ypixmax - clp->ypixmin +1,
340  GL_COLOR);
341 
342 
343  } else if (one_y < clp->ypixmin) {
344  return;
345  } else {
346  /* draw one scanline */
347  glRasterPos2i(clp->xpixmin, one_y);
348  glCopyPixels(SGI(ifp)->mi_xoff + clp->xpixmin,
349  SGI(ifp)->mi_yoff + one_y,
350  clp->xpixmax - clp->xpixmin +1,
351  1,
352  GL_COLOR);
353  }
354 }
355 
356 
357 /*
358  * Note: unlike sgi_xmit_scanlines, this function updates an arbitrary
359  * rectangle of the frame buffer
360  */
361 HIDDEN void
362 ogl_xmit_scanlines(register fb *ifp, int ybase, int nlines, int xbase, int npix)
363 {
364  register int y;
365  register int n;
366  int sw_cmap; /* !0 => needs software color map */
367  struct ogl_clip *clp;
368 
369  /* Caller is expected to handle attaching context, etc. */
370 
371  clp = &(OGL(ifp)->clip);
372 
373  if (OGL(ifp)->soft_cmap_flag && SGI(ifp)->mi_cmap_flag) {
374  sw_cmap = 1;
375  } else {
376  sw_cmap = 0;
377  }
378 
379  if (xbase > clp->xpixmax || ybase > clp->ypixmax)
380  return;
381  if (xbase < clp->xpixmin)
382  xbase = clp->xpixmin;
383  if (ybase < clp->ypixmin)
384  ybase = clp->ypixmin;
385 
386  if ((xbase + npix -1) > clp->xpixmax)
387  npix = clp->xpixmax - xbase + 1;
388  if ((ybase + nlines - 1) > clp->ypixmax)
389  nlines = clp->ypixmax - ybase + 1;
390 
391  if (!OGL(ifp)->use_ext_ctrl) {
392  if (!OGL(ifp)->copy_flag) {
393  /*
394  * Blank out areas of the screen around the image, if
395  * exposed. In COPY mode, this is done in
396  * backbuffer_to_screen().
397  */
398 
399  /* Blank out area left of image */
400  glColor3b(0, 0, 0);
401  if (clp->xscrmin < 0) glRecti(clp->xscrmin - CLIP_XTRA,
402  clp->yscrmin - CLIP_XTRA,
403  CLIP_XTRA,
404  clp->yscrmax + CLIP_XTRA);
405 
406  /* Blank out area below image */
407  if (clp->yscrmin < 0) glRecti(clp->xscrmin - CLIP_XTRA,
408  clp->yscrmin - CLIP_XTRA,
409  clp->xscrmax + CLIP_XTRA,
410  CLIP_XTRA);
411 
412  /* Blank out area right of image */
413  if (clp->xscrmax >= ifp->if_width) glRecti(ifp->if_width - CLIP_XTRA,
414  clp->yscrmin - CLIP_XTRA,
415  clp->xscrmax + CLIP_XTRA,
416  clp->yscrmax + CLIP_XTRA);
417 
418  /* Blank out area above image */
419  if (clp->yscrmax >= ifp->if_height) glRecti(clp->xscrmin - CLIP_XTRA,
420  ifp->if_height- CLIP_XTRA,
421  clp->xscrmax + CLIP_XTRA,
422  clp->yscrmax + CLIP_XTRA);
423 
424  } else if (OGL(ifp)->front_flag) {
425  /* in COPY mode, always draw full sized image into backbuffer.
426  * backbuffer_to_screen() is used to update the front buffer
427  */
428  glDrawBuffer(GL_BACK);
429  OGL(ifp)->front_flag = 0;
430  glMatrixMode(GL_PROJECTION);
431  glPushMatrix(); /* store current view clipping matrix*/
432  glLoadIdentity();
433  glOrtho(-0.25, ((GLdouble) OGL(ifp)->vp_width)-0.25,
434  -0.25, ((GLdouble) OGL(ifp)->vp_height)-0.25,
435  -1.0, 1.0);
436  glPixelZoom(1.0, 1.0);
437  }
438  }
439 
440  if (sw_cmap) {
441  /* Software colormap each line as it's transmitted */
442  register int x;
443  register struct ogl_pixel *oglp;
444  register struct ogl_pixel *op;
445 
446  y = ybase;
447  if (CJDEBUG) printf("Doing sw colormap xmit\n");
448  /* Perform software color mapping into temp scanline */
449  op = SGI(ifp)->mi_scanline;
450  for (n=nlines; n>0; n--, y++) {
451  oglp = (struct ogl_pixel *)&ifp->if_mem[
452  (y*SGI(ifp)->mi_memwidth)*
453  sizeof(struct ogl_pixel) ];
454  for (x=xbase+npix-1; x>=xbase; x--) {
455  op[x].red = CMR(ifp)[oglp[x].red];
456  op[x].green = CMG(ifp)[oglp[x].green];
457  op[x].blue = CMB(ifp)[oglp[x].blue];
458  }
459 
460  glPixelStorei(GL_UNPACK_SKIP_PIXELS, xbase);
461  glRasterPos2i(xbase, y);
462  glDrawPixels(npix, 1, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
463  (const GLvoid *) op);
464 
465  }
466 
467  } else {
468  /* No need for software colormapping */
469 
470  glPixelStorei(GL_UNPACK_ROW_LENGTH, SGI(ifp)->mi_memwidth);
471  glPixelStorei(GL_UNPACK_SKIP_PIXELS, xbase);
472  glPixelStorei(GL_UNPACK_SKIP_ROWS, ybase);
473 
474  glRasterPos2i(xbase, ybase);
475  glDrawPixels(npix, nlines, GL_BGRA_EXT, GL_UNSIGNED_BYTE,
476  (const GLvoid *) ifp->if_mem);
477  }
478 }
479 
480 
481 HIDDEN void
482 ogl_cminit(register fb *ifp)
483 {
484  register int i;
485 
486  for (i = 0; i < 256; i++) {
487  CMR(ifp)[i] = i;
488  CMG(ifp)[i] = i;
489  CMB(ifp)[i] = i;
490  }
491 }
492 
493 
494 /************************************************************************/
495 /******************* Shared Memory Support ******************************/
496 /************************************************************************/
497 
498 /**
499  * not changed from sgi_getmem.
500  *
501  * Because there is no hardware zoom or pan, we need to repaint the
502  * screen (with big pixels) to implement these operations. This means
503  * that the actual "contents" of the frame buffer need to be stored
504  * somewhere else. If possible, we allocate a shared memory segment
505  * to contain that image. This has several advantages, the most
506  * important being that when operating the display in 12-bit output
507  * mode, pixel-readbacks still give the full 24-bits of color. System
508  * V shared memory persists until explicitly killed, so this also
509  * means that in MEX mode, the previous contents of the frame buffer
510  * still exist, and can be again accessed, even though the MEX windows
511  * are transient, per-process.
512  *
513  * There are a few oddities, however. The worst is that System V will
514  * not allow the break (see sbrk(2)) to be set above a shared memory
515  * segment, and shmat(2) does not seem to allow the selection of any
516  * reasonable memory address (like 6 Mbytes up) for the shared memory.
517  * In the initial version of this routine, that prevented subsequent
518  * calls to malloc() from succeeding, quite a drawback. The
519  * work-around used here is to increase the current break to a large
520  * value, attach to the shared memory, and then return the break to
521  * its original value. This should allow most reasonable requests for
522  * memory to be satisfied. In special cases, the values used here
523  * might need to be increased.
524  */
525 HIDDEN int
526 ogl_getmem(fb *ifp)
527 {
528  int pixsize;
529  int size;
530  int i;
531  char *sp;
532  int new_mem = 0;
533  int shm_result = 0;
534 
535  errno = 0;
536 
537  if ((ifp->if_mode & MODE_1MASK) == MODE_1MALLOC) {
538  /*
539  * In this mode, only malloc as much memory as is needed.
540  */
541  SGI(ifp)->mi_memwidth = ifp->if_width;
542  pixsize = ifp->if_height * ifp->if_width * sizeof(struct ogl_pixel);
543  size = pixsize + sizeof(struct ogl_cmap);
544 
545  sp = (char *)calloc(1, size);
546  if (sp == 0) {
547  fb_log("ogl_getmem: frame buffer memory malloc failed\n");
548  goto fail;
549  }
550  new_mem = 1;
551  goto success;
552  }
553 
554  /* The shared memory section never changes size */
555  SGI(ifp)->mi_memwidth = ifp->if_max_width;
556 
557  /*
558  * On some platforms lrectwrite() runs off the end! So, provide a
559  * pad area of 2 scanlines. (1 line is enough, but this avoids
560  * risk of damage to colormap table.)
561  */
562  pixsize = (ifp->if_max_height+2) * ifp->if_max_width *
563  sizeof(struct ogl_pixel);
564 
565  size = pixsize + sizeof(struct ogl_cmap);
566 
567 
568  shm_result = bu_shmget(&(SGI(ifp)->mi_shmid), &sp, SHMEM_KEY, (size_t)size);
569 
570  if (shm_result == 1) goto fail;
571  if (shm_result == -1) new_mem = 1;
572 
573 success:
574  ifp->if_mem = sp;
575  ifp->if_cmap = sp + pixsize; /* cmap at end of area */
576  i = CMB(ifp)[255]; /* try to deref last word */
577  CMB(ifp)[255] = i;
578 
579  /* Provide non-black colormap on creation of new shared mem */
580  if (new_mem)
581  ogl_cminit(ifp);
582  return 0;
583 fail:
584  fb_log("ogl_getmem: Unable to attach to shared memory.\n");
585  if ((sp = (char *)calloc(1, size)) == NULL) {
586  fb_log("ogl_getmem: malloc failure\n");
587  return -1;
588  }
589  new_mem = 1;
590  goto success;
591 }
592 
593 
594 void
595 ogl_zapmem(void)
596 {
597  int shmid;
598  int i;
599 
600  if ((shmid = shmget(SHMEM_KEY, 0, 0)) < 0) {
601  fb_log("ogl_zapmem shmget failed, errno=%d\n", errno);
602  return;
603  }
604 
605  i = shmctl(shmid, IPC_RMID, 0);
606  if (i < 0) {
607  fb_log("ogl_zapmem shmctl failed, errno=%d\n", errno);
608  return;
609  }
610  fb_log("if_ogl: shared memory released\n");
611 }
612 
613 
614 /**
615  * Given:- the size of the viewport in pixels (vp_width, vp_height)
616  * - the size of the framebuffer image (if_width, if_height)
617  * - the current view center (if_xcenter, if_ycenter)
618  * - the current zoom (if_xzoom, if_yzoom)
619  * Calculate:
620  * - the position of the viewport in image space
621  * (xscrmin, xscrmax, yscrmin, yscrmax)
622  * - the portion of the image which is visible in the viewport
623  * (xpixmin, xpixmax, ypixmin, ypixmax)
624  */
625 void
626 ogl_clipper(register fb *ifp)
627 {
628  register struct ogl_clip *clp;
629  register int i;
630  double pixels;
631 
632  clp = &(OGL(ifp)->clip);
633 
634  i = OGL(ifp)->vp_width/(2*ifp->if_xzoom);
635  clp->xscrmin = ifp->if_xcenter - i;
636  i = OGL(ifp)->vp_width/ifp->if_xzoom;
637  clp->xscrmax = clp->xscrmin + i;
638  pixels = (double) i;
639  clp->oleft = ((double) clp->xscrmin) - 0.25*pixels/((double) OGL(ifp)->vp_width);
640  clp->oright = clp->oleft + pixels;
641 
642  i = OGL(ifp)->vp_height/(2*ifp->if_yzoom);
643  clp->yscrmin = ifp->if_ycenter - i;
644  i = OGL(ifp)->vp_height/ifp->if_yzoom;
645  clp->yscrmax = clp->yscrmin + i;
646  pixels = (double) i;
647  clp->obottom = ((double) clp->yscrmin) - 0.25*pixels/((double) OGL(ifp)->vp_height);
648  clp->otop = clp->obottom + pixels;
649 
650  clp->xpixmin = clp->xscrmin;
651  clp->xpixmax = clp->xscrmax;
652  clp->ypixmin = clp->yscrmin;
653  clp->ypixmax = clp->yscrmax;
654 
655  if (clp->xpixmin < 0) {
656  clp->xpixmin = 0;
657  }
658 
659  if (clp->ypixmin < 0) {
660  clp->ypixmin = 0;
661  }
662 
663  /* In copy mode, the backbuffer copy image is limited
664  * to the viewport size; use that for clipping.
665  * Otherwise, use size of framebuffer memory segment
666  */
667  if (OGL(ifp)->copy_flag) {
668  if (clp->xpixmax > OGL(ifp)->vp_width-1) {
669  clp->xpixmax = OGL(ifp)->vp_width-1;
670  }
671  if (clp->ypixmax > OGL(ifp)->vp_height-1) {
672  clp->ypixmax = OGL(ifp)->vp_height-1;
673  }
674  } else {
675  if (clp->xpixmax > ifp->if_width-1) {
676  clp->xpixmax = ifp->if_width-1;
677  }
678  if (clp->ypixmax > ifp->if_height-1) {
679  clp->ypixmax = ifp->if_height-1;
680  }
681  }
682 
683 }
684 
685 
686 HIDDEN void
687 expose_callback(fb *ifp)
688 {
689  XWindowAttributes xwa;
690  struct ogl_clip *clp;
691 
692  if (CJDEBUG) fb_log("entering expose_callback()\n");
693 
694  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
695  fb_log("Warning, expose_callback: glXMakeCurrent unsuccessful.\n");
696  }
697 
698  if (OGL(ifp)->firstTime) {
699 
700  OGL(ifp)->firstTime = 0;
701 
702  /* just in case the configuration is double buffered but
703  * we want to pretend it's not
704  */
705 
706  if (!SGI(ifp)->mi_doublebuffer) {
707  glDrawBuffer(GL_FRONT);
708  }
709 
710  if ((ifp->if_mode & MODE_4MASK) == MODE_4NODITH) {
711  glDisable(GL_DITHER);
712  }
713 
714  /* set copy mode if possible and requested */
715  if (SGI(ifp)->mi_doublebuffer &&
716  ((ifp->if_mode & MODE_11MASK)==MODE_11COPY)) {
717  /* Copy mode only works if there are two
718  * buffers to use. It conflicts with
719  * double buffering
720  */
721  OGL(ifp)->copy_flag = 1;
722  SGI(ifp)->mi_doublebuffer = 0;
723  OGL(ifp)->front_flag = 1;
724  glDrawBuffer(GL_FRONT);
725  } else {
726  OGL(ifp)->copy_flag = 0;
727  }
728 
729  XGetWindowAttributes(OGL(ifp)->dispp, OGL(ifp)->wind, &xwa);
730  OGL(ifp)->win_width = xwa.width;
731  OGL(ifp)->win_height = xwa.height;
732 
733  /* clear entire window */
734  glViewport(0, 0, OGL(ifp)->win_width, OGL(ifp)->win_height);
735  glClearColor(0, 0, 0, 0);
736  glClear(GL_COLOR_BUFFER_BIT);
737 
738  /* Set normal viewport size to minimum of actual window
739  * size and requested framebuffer size
740  */
741  OGL(ifp)->vp_width = (OGL(ifp)->win_width < ifp->if_width) ?
742  OGL(ifp)->win_width : ifp->if_width;
743  OGL(ifp)->vp_height = (OGL(ifp)->win_height < ifp->if_height) ?
744  OGL(ifp)->win_height : ifp->if_height;
745  ifp->if_xcenter = OGL(ifp)->vp_width/2;
746  ifp->if_ycenter = OGL(ifp)->vp_height/2;
747 
748  /* center viewport in window */
749  SGI(ifp)->mi_xoff=(OGL(ifp)->win_width-OGL(ifp)->vp_width)/2;
750  SGI(ifp)->mi_yoff=(OGL(ifp)->win_height-OGL(ifp)->vp_height)/2;
751  glViewport(SGI(ifp)->mi_xoff,
752  SGI(ifp)->mi_yoff,
753  OGL(ifp)->vp_width,
754  OGL(ifp)->vp_height);
755  /* initialize clipping planes and zoom */
756  ogl_clipper(ifp);
757  clp = &(OGL(ifp)->clip);
758  glMatrixMode(GL_PROJECTION);
759  glLoadIdentity();
760  glOrtho(clp->oleft, clp->oright, clp->obottom, clp->otop,
761  -1.0, 1.0);
762  glPixelZoom((float) ifp->if_xzoom, (float) ifp->if_yzoom);
763  } else if ((OGL(ifp)->win_width > ifp->if_width) ||
764  (OGL(ifp)->win_height > ifp->if_height)) {
765  /* clear whole buffer if window larger than framebuffer */
766  if (OGL(ifp)->copy_flag && !OGL(ifp)->front_flag) {
767  glDrawBuffer(GL_FRONT);
768  glViewport(0, 0, OGL(ifp)->win_width,
769  OGL(ifp)->win_height);
770  glClearColor(0, 0, 0, 0);
771  glClear(GL_COLOR_BUFFER_BIT);
772  glDrawBuffer(GL_BACK);
773  } else {
774  glViewport(0, 0, OGL(ifp)->win_width,
775  OGL(ifp)->win_height);
776  glClearColor(0, 0, 0, 0);
777  glClear(GL_COLOR_BUFFER_BIT);
778  }
779  /* center viewport */
780  glViewport(SGI(ifp)->mi_xoff,
781  SGI(ifp)->mi_yoff,
782  OGL(ifp)->vp_width,
783  OGL(ifp)->vp_height);
784  }
785 
786  /* repaint entire image */
787  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
788  if (SGI(ifp)->mi_doublebuffer) {
789  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
790  } else if (OGL(ifp)->copy_flag) {
791  backbuffer_to_screen(ifp, -1);
792  }
793 
794  if (CJDEBUG) {
795  int dbb, db, view[4], getster, getaux;
796  glGetIntegerv(GL_VIEWPORT, view);
797  glGetIntegerv(GL_DOUBLEBUFFER, &dbb);
798  glGetIntegerv(GL_DRAW_BUFFER, &db);
799  fb_log("Viewport: x %d y %d width %d height %d\n", view[0],
800  view[1], view[2], view[3]);
801  fb_log("expose: double buffered: %d, draw buffer %d\n", dbb, db);
802  fb_log("front %d\tback%d\n", GL_FRONT, GL_BACK);
803  glGetIntegerv(GL_STEREO, &getster);
804  glGetIntegerv(GL_AUX_BUFFERS, &getaux);
805  fb_log("double %d, stereo %d, aux %d\n", dbb, getster, getaux);
806  }
807 
808  /* unattach context for other threads to use */
809  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
810 }
811 
812 
813 int
814 ogl_configureWindow(fb *ifp, int width, int height)
815 {
816  if (width == OGL(ifp)->win_width &&
817  height == OGL(ifp)->win_height)
818  return 1;
819 
820  ifp->if_width = ifp->if_max_width = width;
821  ifp->if_height = ifp->if_max_height = height;
822 
823  OGL(ifp)->win_width = OGL(ifp)->vp_width = width;
824  OGL(ifp)->win_height = OGL(ifp)->vp_height = height;
825 
826  ifp->if_zoomflag = 0;
827  ifp->if_xzoom = 1;
828  ifp->if_yzoom = 1;
829  ifp->if_xcenter = width/2;
830  ifp->if_ycenter = height/2;
831 
832  ogl_getmem(ifp);
833  ogl_clipper(ifp);
834  return 0;
835 }
836 
837 
838 HIDDEN void
839 ogl_do_event(fb *ifp)
840 {
841  XEvent event;
842 
843  while (XCheckWindowEvent(OGL(ifp)->dispp, OGL(ifp)->wind,
844  OGL(ifp)->event_mask, &event)) {
845  switch (event.type) {
846  case Expose:
847  if (!OGL(ifp)->use_ext_ctrl)
848  expose_callback(ifp);
849  break;
850  case ButtonPress:
851  {
852  int button = (int) event.xbutton.button;
853  if (button == Button1) {
854  /* Check for single button mouse remap.
855  * ctrl-1 => 2
856  * meta-1 => 3
857  * cmdkey => 3
858  */
859  if (event.xbutton.state & ControlMask) {
860  button = Button2;
861  } else if (event.xbutton.state & Mod1Mask) {
862  button = Button3;
863  } else if (event.xbutton.state & Mod2Mask) {
864  button = Button3;
865  }
866  }
867 
868  switch (button) {
869  case Button1:
870  break;
871  case Button2:
872  {
873  int x, y;
874  register struct ogl_pixel *oglp;
875 
876  x = event.xbutton.x;
877  y = ifp->if_height - event.xbutton.y - 1;
878 
879  if (x < 0 || y < 0) {
880  fb_log("No RGB (outside image viewport)\n");
881  break;
882  }
883 
884  oglp = (struct ogl_pixel *)&ifp->if_mem[
885  (y*SGI(ifp)->mi_memwidth)*
886  sizeof(struct ogl_pixel) ];
887 
888  fb_log("At image (%d, %d), real RGB=(%3d %3d %3d)\n",
889  x, y, (int)oglp[x].red, (int)oglp[x].green, (int)oglp[x].blue);
890 
891  break;
892  }
893  case Button3:
894  OGL(ifp)->alive = 0;
895  break;
896  default:
897  fb_log("unhandled mouse event\n");
898  break;
899  }
900  break;
901  }
902  case ConfigureNotify:
903  {
904  XConfigureEvent *conf = (XConfigureEvent *)&event;
905 
906  if (conf->width == OGL(ifp)->win_width &&
907  conf->height == OGL(ifp)->win_height)
908  return;
909 
910  ogl_configureWindow(ifp, conf->width, conf->height);
911  }
912  default:
913  break;
914  }
915  }
916 }
917 
918 
919 /**
920  * Select an appropriate visual, and set flags.
921  *
922  * The user requires support for:
923  * -OpenGL rendering in RGBA mode
924  *
925  * The user may desire support for:
926  * -a single-buffered OpenGL context
927  * -a double-buffered OpenGL context
928  * -hardware colormapping (DirectColor)
929  *
930  * We first try to satisfy all requirements and desires. If that
931  * fails, we remove the desires one at a time until we succeed or
932  * until only requirements are left. If at any stage more than one
933  * visual meets the current criteria, the visual with the greatest
934  * depth is chosen.
935  *
936  * The following flags are set:
937  * SGI(ifp)->mi_doublebuffer
938  * OGL(ifp)->soft_cmap_flag
939  *
940  * Return NULL on failure.
941  */
942 HIDDEN XVisualInfo *
943 fb_ogl_choose_visual(fb *ifp)
944 {
945 
946  XVisualInfo *vip, *vibase, *maxvip, _template;
947 #define NGOOD 200
948  int good[NGOOD];
949  int num, i, j;
950  int m_hard_cmap, m_sing_buf, m_doub_buf;
951  int use, rgba, dbfr;
952 
953  m_hard_cmap = ((ifp->if_mode & MODE_7MASK)==MODE_7NORMAL);
954  m_sing_buf = ((ifp->if_mode & MODE_9MASK)==MODE_9SINGLEBUF);
955  m_doub_buf = !m_sing_buf;
956 
957  memset((void *)&_template, 0, sizeof(XVisualInfo));
958 
959  /* get a list of all visuals on this display */
960  vibase = XGetVisualInfo(OGL(ifp)->dispp, 0, &_template, &num);
961  while (1) {
962 
963  /* search for all visuals matching current criteria */
964  for (i = 0, j = 0, vip=vibase; i < num; i++, vip++) {
965  /* requirements */
966  glXGetConfig(OGL(ifp)->dispp, vip, GLX_USE_GL, &use);
967  if (!use) {
968  continue;
969  }
970  glXGetConfig(OGL(ifp)->dispp, vip, GLX_RGBA, &rgba);
971  if (!rgba) {
972  continue;
973  }
974  /* desires */
975  /* X_CreateColormap needs a DirectColor visual */
976  /* There should be some way of handling this with TrueColor,
977  * for example:
978  visual id: 0x50
979  class: TrueColor
980  depth: 24 planes
981  available colormap entries: 256 per subfield
982  red, green, blue masks: 0xff0000, 0xff00, 0xff
983  significant bits in color specification: 8 bits
984  */
985  if ((m_hard_cmap) && (vip->class != DirectColor)) {
986  continue;
987  }
988  if ((m_hard_cmap) && (vip->colormap_size < 256)) {
989  continue;
990  }
991  glXGetConfig(OGL(ifp)->dispp, vip, GLX_DOUBLEBUFFER, &dbfr);
992  if ((m_doub_buf) && (!dbfr)) {
993  continue;
994  }
995  if ((m_sing_buf) && (dbfr)) {
996  continue;
997  }
998 
999  /* this visual meets criteria */
1000  if (j >= NGOOD-1) {
1001  fb_log("fb_ogl_open: More than %d candidate visuals!\n", NGOOD);
1002  break;
1003  }
1004  good[j++] = i;
1005  }
1006 
1007  /* from list of acceptable visuals,
1008  * choose the visual with the greatest depth */
1009  if (j >= 1) {
1010  maxvip = vibase + good[0];
1011  for (i = 1; i < j; i++) {
1012  vip = vibase + good[i];
1013  if (vip->depth > maxvip->depth) {
1014  maxvip = vip;
1015  }
1016  }
1017  /* set flags and return choice */
1018  OGL(ifp)->soft_cmap_flag = !m_hard_cmap;
1019  SGI(ifp)->mi_doublebuffer = m_doub_buf;
1020  return maxvip;
1021  }
1022 
1023  /* if no success at this point,
1024  * relax one of the criteria and try again.
1025  */
1026  if (m_hard_cmap) {
1027  /* relax hardware colormap requirement */
1028  m_hard_cmap = 0;
1029  fb_log("fb_ogl_open: hardware colormapping not available. Using software colormap.\n");
1030  } else if (m_sing_buf) {
1031  /* relax single buffering requirement.
1032  * no need for any warning - we'll just use
1033  * the front buffer
1034  */
1035  m_sing_buf = 0;
1036  } else if (m_doub_buf) {
1037  /* relax double buffering requirement. */
1038  m_doub_buf = 0;
1039  fb_log("fb_ogl_open: double buffering not available. Using single buffer.\n");
1040  } else {
1041  /* nothing else to relax */
1042  return NULL;
1043  }
1044 
1045  }
1046 
1047 }
1048 
1049 
1050 /**
1051  * Check for a color map being linear in R, G, and B. Returns 1 for
1052  * linear map, 0 for non-linear map (i.e., non-identity map).
1053  */
1054 HIDDEN int
1055 is_linear_cmap(register fb *ifp)
1056 {
1057  register int i;
1058 
1059  for (i = 0; i < 256; i++) {
1060  if (CMR(ifp)[i] != i) return 0;
1061  if (CMG(ifp)[i] != i) return 0;
1062  if (CMB(ifp)[i] != i) return 0;
1063  }
1064  return 1;
1065 }
1066 
1067 
1068 HIDDEN int
1069 fb_ogl_open(fb *ifp, const char *file, int width, int height)
1070 {
1071  static char title[128];
1072  int mode, i, direct;
1073  long valuemask;
1074  XSetWindowAttributes swa;
1075 
1076  FB_CK_FB(ifp);
1077 
1078  /*
1079  * First, attempt to determine operating mode for this open,
1080  * based upon the "unit number" or flags.
1081  * file = "/dev/ogl###"
1082  */
1083  mode = MODE_2LINGERING;
1084 
1085  if (file != NULL) {
1086  const char *cp;
1087  char modebuf[80];
1088  char *mp;
1089  int alpha;
1090  struct modeflags *mfp;
1091 
1092  if (bu_strncmp(file, ifp->if_name, strlen(ifp->if_name))) {
1093  /* How did this happen? */
1094  mode = 0;
1095  } else {
1096  /* Parse the options */
1097  alpha = 0;
1098  mp = &modebuf[0];
1099  cp = &file[8];
1100  while (*cp != '\0' && !isspace((int)(*cp))) {
1101  *mp++ = *cp; /* copy it to buffer */
1102  if (isdigit((int)(*cp))) {
1103  cp++;
1104  continue;
1105  }
1106  alpha++;
1107  for (mfp = modeflags; mfp->c != '\0'; mfp++) {
1108  if (mfp->c == *cp) {
1109  mode = (mode&~mfp->mask)|mfp->value;
1110  break;
1111  }
1112  }
1113  if (mfp->c == '\0' && *cp != '-') {
1114  fb_log("if_ogl: unknown option '%c' ignored\n", *cp);
1115  }
1116  cp++;
1117  }
1118  *mp = '\0';
1119  if (!alpha) {
1120  mode |= atoi(modebuf);
1121  }
1122  }
1123 
1124  if ((mode & MODE_15MASK) == MODE_15ZAP) {
1125  /* Only task: Attempt to release shared memory segment */
1126  ogl_zapmem();
1127  return -1;
1128  }
1129  }
1130 #if DIRECT_COLOR_VISUAL_ALLOWED
1131  ifp->if_mode = mode;
1132 #else
1133  ifp->if_mode = mode|MODE_7SWCMAP;
1134 #endif
1135 
1136  /*
1137  * Allocate extension memory sections,
1138  * addressed by SGI(ifp)->mi_xxx and OGL(ifp)->xxx
1139  */
1140 
1141  if ((SGIL(ifp) = (char *)calloc(1, sizeof(struct sgiinfo))) == NULL) {
1142  fb_log("fb_ogl_open: sgiinfo malloc failed\n");
1143  return -1;
1144  }
1145  if ((OGLL(ifp) = (char *)calloc(1, sizeof(struct oglinfo))) == NULL) {
1146  fb_log("fb_ogl_open: oglinfo malloc failed\n");
1147  return -1;
1148  }
1149 
1150  SGI(ifp)->mi_shmid = -1; /* indicate no shared memory */
1151 
1152  /* the Silicon Graphics Library Window management routines use
1153  * shared memory. This causes lots of problems when you want to
1154  * pass a window structure to a child process. One hack to get
1155  * around this is to immediately fork and create a child process
1156  * and sleep until the child sends a kill signal to the parent
1157  * process. (in FBCLOSE) This allows us to use the traditional fb
1158  * utility programs as well as allow the frame buffer window to
1159  * remain around until killed by the menu subsystem.
1160  */
1161 
1162  if ((ifp->if_mode & MODE_2MASK) == MODE_2LINGERING) {
1163  /* save parent pid for later signalling */
1164  SGI(ifp)->mi_parent = bu_process_id();
1165 
1166  signal(SIGUSR1, sigkid);
1167  }
1168 
1169  /* use defaults if invalid width and height specified */
1170  if (width <= 0)
1171  width = ifp->if_width;
1172  if (height <= 0)
1173  height = ifp->if_height;
1174  /* use max values if width and height are greater */
1175  if (width > ifp->if_max_width)
1176  width = ifp->if_max_width;
1177  if (height > ifp->if_max_height)
1178  height = ifp->if_max_height;
1179 
1180  ifp->if_width = width;
1181  ifp->if_height = height;
1182 
1183  SGI(ifp)->mi_curs_on = 1;
1184 
1185  /* Build a descriptive window title bar */
1186  (void)snprintf(title, 128, "BRL-CAD /dev/ogl %s, %s",
1187  ((ifp->if_mode & MODE_2MASK) == MODE_2TRANSIENT) ?
1188  "Transient Win":
1189  "Lingering Win",
1190  ((ifp->if_mode & MODE_1MASK) == MODE_1MALLOC) ?
1191  "Private Mem" :
1192  "Shared Mem");
1193 
1194  /* initialize window state variables before calling ogl_getmem */
1195  ifp->if_zoomflag = 0;
1196  ifp->if_xzoom = 1; /* for zoom fakeout */
1197  ifp->if_yzoom = 1; /* for zoom fakeout */
1198  ifp->if_xcenter = width/2;
1199  ifp->if_ycenter = height/2;
1200  SGI(ifp)->mi_pid = bu_process_id();
1201 
1202  /* Attach to shared memory, potentially with a screen repaint */
1203  if (ogl_getmem(ifp) < 0)
1204  return -1;
1205 
1206  /* Open an X connection to the display. Sending NULL to XOpenDisplay
1207  tells it to use the DISPLAY environment variable. */
1208  if ((OGL(ifp)->dispp = XOpenDisplay(NULL)) == NULL) {
1209  fb_log("fb_ogl_open: Failed to open display. Check DISPLAY environment variable.\n");
1210  return -1;
1211  }
1212  ifp->if_selfd = ConnectionNumber(OGL(ifp)->dispp);
1213  if (CJDEBUG) {
1214  printf("Connection opened to X display on fd %d.\n",
1215  ConnectionNumber(OGL(ifp)->dispp));
1216  }
1217 
1218  /* Choose an appropriate visual. */
1219  if ((OGL(ifp)->vip = fb_ogl_choose_visual(ifp)) == NULL) {
1220  fb_log("fb_ogl_open: Couldn't find an appropriate visual. Exiting.\n");
1221  return -1;
1222  }
1223 
1224  /* Open an OpenGL context with this visual*/
1225  OGL(ifp)->glxc = glXCreateContext(OGL(ifp)->dispp, OGL(ifp)->vip, 0, GL_TRUE /* direct context */);
1226  if (OGL(ifp)->glxc == NULL) {
1227  fb_log("ERROR: Couldn't create an OpenGL context!\n");
1228  return -1;
1229  }
1230 
1231  direct = glXIsDirect(OGL(ifp)->dispp, OGL(ifp)->glxc);
1232  if (CJDEBUG) {
1233  fb_log("Framebuffer drawing context is %s.\n", direct ? "direct" : "indirect");
1234  }
1235 
1236  /* Create a colormap for this visual */
1237  SGI(ifp)->mi_cmap_flag = !is_linear_cmap(ifp);
1238  if (!OGL(ifp)->soft_cmap_flag) {
1239  OGL(ifp)->xcmap = XCreateColormap(OGL(ifp)->dispp,
1240  RootWindow(OGL(ifp)->dispp,
1241  OGL(ifp)->vip->screen),
1242  OGL(ifp)->vip->visual,
1243  AllocAll);
1244  /* initialize virtual colormap - it will be loaded into
1245  * the hardware. This code has not yet been tested.
1246  */
1247  if (CJDEBUG) printf("Loading read/write colormap.\n");
1248  for (i = 0; i < 256; i++) {
1249  color_cell[i].pixel = i;
1250  color_cell[i].red = CMR(ifp)[i];
1251  color_cell[i].green = CMG(ifp)[i];
1252  color_cell[i].blue = CMB(ifp)[i];
1253  color_cell[i].flags = DoRed | DoGreen | DoBlue;
1254  }
1255  XStoreColors(OGL(ifp)->dispp, OGL(ifp)->xcmap, color_cell, 256);
1256  } else {
1257  /* read only colormap */
1258  if (CJDEBUG) {
1259  printf("Allocating read-only colormap.");
1260  }
1261  OGL(ifp)->xcmap = XCreateColormap(OGL(ifp)->dispp,
1262  RootWindow(OGL(ifp)->dispp,
1263  OGL(ifp)->vip->screen),
1264  OGL(ifp)->vip->visual,
1265  AllocNone);
1266  }
1267 
1268  XSync(OGL(ifp)->dispp, 0);
1269 
1270  /* Create a window. */
1271  memset(&swa, 0, sizeof(swa));
1272 
1273  valuemask = CWBackPixel | CWBorderPixel | CWEventMask | CWColormap;
1274 
1275  swa.background_pixel = BlackPixel(OGL(ifp)->dispp,
1276  OGL(ifp)->vip->screen);
1277  swa.border_pixel = BlackPixel(OGL(ifp)->dispp,
1278  OGL(ifp)->vip->screen);
1279  swa.event_mask = OGL(ifp)->event_mask =
1280  ExposureMask | KeyPressMask | KeyReleaseMask |
1281  ButtonPressMask | ButtonReleaseMask;
1282  swa.colormap = OGL(ifp)->xcmap;
1283 
1284 #define XCreateWindowDebug(display, parent, x, y, width, height, \
1285  border_width, depth, class, visual, valuemask, \
1286  attributes) \
1287  (printf("XCreateWindow(display = %08X, \n", (long)display), \
1288  printf(" parent = %08X, \n", (long)parent), \
1289  printf(" x = %d, \n", x), \
1290  printf(" y = %d, \n", y), \
1291  printf(" width = %d, \n", width), \
1292  printf(" height = %d, \n", height), \
1293  printf(" border_width = %d, \n", border_width), \
1294  printf(" depth = %d, \n", depth), \
1295  printf(" class = %d, \n", class), \
1296  printf(" visual = %08X, \n", (long)visual), \
1297  printf(" valuemask = %08X, \n", valuemask), \
1298  printf(" attributes = {"), \
1299  (valuemask & CWBackPixmap) ? printf(" background_pixmap = %08X ", (long)((attributes)->background_pixmap)) : 0, \
1300  (valuemask & CWBackPixel) ? printf(" background_pixel = %08X ", (attributes)->background_pixel) : 0, \
1301  (valuemask & CWBorderPixmap) ? printf(" border_pixmap = %08X ", (long)((attributes)->border_pixmap)) : 0, \
1302  (valuemask & CWBorderPixel) ? printf(" border_pixel = %08X ", (attributes)->border_pixel) : 0, \
1303  (valuemask & CWBitGravity) ? printf(" bit_gravity = %d ", (attributes)->bit_gravity) : 0, \
1304  (valuemask & CWWinGravity) ? printf(" win_gravity = %d ", (attributes)->win_gravity) : 0, \
1305  (valuemask & CWBackingStore) ? printf(" backing_store = %d ", (attributes)->backing_store) : 0, \
1306  (valuemask & CWBackingPlanes) ? printf(" backing_planes = %d ", (attributes)->backing_planes) : 0, \
1307  (valuemask & CWBackingPixel) ? printf(" backing_pixel = %08X ", (attributes)->backing_pixel) : 0, \
1308  (valuemask & CWOverrideRedirect) ? printf(" override_redirect = %d ", (attributes)->override_redirect) : 0, \
1309  (valuemask & CWSaveUnder) ? printf(" save_under = %d ", (attributes)->save_under) : 0, \
1310  (valuemask & CWEventMask) ? printf(" event_mask = %08X ", (attributes)->event_mask) : 0, \
1311  (valuemask & CWDontPropagate) ? printf(" do_not_propagate_mask = %08X ", (attributes)->do_not_propagate_mask) : 0, \
1312  (valuemask & CWColormap) ? printf(" colormap = %08X ", (long)((attributes)->colormap)) : 0, \
1313  (valuemask & CWCursor) ? printf(" cursor = %08X ", (long)((attributes)->cursor)) : 0, \
1314  printf(" }\n")) > 0 ? XCreateWindow(display, parent, x, y, width, height, border_width, depth, class, visual, valuemask, attributes) : -1;
1315 
1316  OGL(ifp)->wind = XCreateWindow(OGL(ifp)->dispp,
1317  RootWindow(OGL(ifp)->dispp,
1318  OGL(ifp)->vip->screen),
1319  0, 0, ifp->if_width, ifp->if_height, 0,
1320  OGL(ifp)->vip->depth,
1321  InputOutput,
1322  OGL(ifp)->vip->visual,
1323  valuemask, &swa);
1324 
1325  XStoreName(OGL(ifp)->dispp, OGL(ifp)->wind, title);
1326 
1327  /* count windows */
1328  ogl_nwindows++;
1329  XMapRaised(OGL(ifp)->dispp, OGL(ifp)->wind);
1330 
1331  OGL(ifp)->alive = 1;
1332  OGL(ifp)->firstTime = 1;
1333 
1334  /* Loop through events until first exposure event is processed */
1335  while (OGL(ifp)->firstTime == 1)
1336  ogl_do_event(ifp);
1337 
1338  return 0;
1339 }
1340 
1341 
1342 
1343 int
1344 _ogl_open_existing(fb *ifp, Display *dpy, Window win, Colormap cmap, XVisualInfo *vip, int width, int height, GLXContext glxc, int double_buffer, int soft_cmap)
1345 {
1346 
1347  /*XXX for now use private memory */
1348  ifp->if_mode = MODE_1MALLOC;
1349 
1350  /*
1351  * Allocate extension memory sections,
1352  * addressed by SGI(ifp)->mi_xxx and OGL(ifp)->xxx
1353  */
1354 
1355  if ((SGIL(ifp) = (char *)calloc(1, sizeof(struct sgiinfo))) == NULL) {
1356  fb_log("fb_ogl_open: sgiinfo malloc failed\n");
1357  return -1;
1358  }
1359  if ((OGLL(ifp) = (char *)calloc(1, sizeof(struct oglinfo))) == NULL) {
1360  fb_log("fb_ogl_open: oglinfo malloc failed\n");
1361  return -1;
1362  }
1363 
1364  OGL(ifp)->use_ext_ctrl = 1;
1365 
1366  SGI(ifp)->mi_shmid = -1; /* indicate no shared memory */
1367  ifp->if_width = ifp->if_max_width = width;
1368  ifp->if_height = ifp->if_max_height = height;
1369 
1370  OGL(ifp)->win_width = OGL(ifp)->vp_width = width;
1371  OGL(ifp)->win_height = OGL(ifp)->vp_height = height;
1372 
1373  SGI(ifp)->mi_curs_on = 1;
1374 
1375  /* initialize window state variables before calling ogl_getmem */
1376  ifp->if_zoomflag = 0;
1377  ifp->if_xzoom = 1; /* for zoom fakeout */
1378  ifp->if_yzoom = 1; /* for zoom fakeout */
1379  ifp->if_xcenter = width/2;
1380  ifp->if_ycenter = height/2;
1381  SGI(ifp)->mi_pid = bu_process_id();
1382 
1383  /* Attach to shared memory, potentially with a screen repaint */
1384  if (ogl_getmem(ifp) < 0)
1385  return -1;
1386 
1387  OGL(ifp)->dispp = dpy;
1388  ifp->if_selfd = ConnectionNumber(OGL(ifp)->dispp);
1389 
1390  OGL(ifp)->vip = vip;
1391  OGL(ifp)->glxc = glxc;
1392  SGI(ifp)->mi_cmap_flag = !is_linear_cmap(ifp);
1393  OGL(ifp)->soft_cmap_flag = soft_cmap;
1394  SGI(ifp)->mi_doublebuffer = double_buffer;
1395  OGL(ifp)->xcmap = cmap;
1396 
1397  OGL(ifp)->wind = win;
1398  ++ogl_nwindows;
1399 
1400  OGL(ifp)->alive = 1;
1401  OGL(ifp)->firstTime = 1;
1402 
1403  ogl_clipper(ifp);
1404 
1405  return 0;
1406 }
1407 
1409 ogl_get_fbps(uint32_t magic)
1410 {
1411  struct fb_platform_specific *fb_ps = NULL;
1412  struct ogl_fb_info *data = NULL;
1413  BU_GET(fb_ps, struct fb_platform_specific);
1414  BU_GET(data, struct ogl_fb_info);
1415  fb_ps->magic = magic;
1416  fb_ps->data = data;
1417  return fb_ps;
1418 }
1419 
1420 
1421 HIDDEN void
1422 ogl_put_fbps(struct fb_platform_specific *fbps)
1423 {
1424  BU_CKMAG(fbps, FB_OGL_MAGIC, "ogl framebuffer");
1425  BU_PUT(fbps->data, struct ogl_fb_info);
1426  BU_PUT(fbps, struct fb_platform_specific);
1427  return;
1428 }
1429 
1430 HIDDEN int
1431 ogl_open_existing(fb *ifp, int width, int height, struct fb_platform_specific *fb_p)
1432 {
1433  struct ogl_fb_info *ogl_internal = (struct ogl_fb_info *)fb_p->data;
1434  BU_CKMAG(fb_p, FB_OGL_MAGIC, "ogl framebuffer");
1435  return _ogl_open_existing(ifp, ogl_internal->dpy, ogl_internal->win,
1436  ogl_internal->cmap, ogl_internal->vip, width, height, ogl_internal->glxc,
1437  ogl_internal->double_buffer, ogl_internal->soft_cmap);
1438 
1439  return 0;
1440 }
1441 
1442 
1443 HIDDEN int
1444 ogl_final_close(fb *ifp)
1445 {
1446 
1447  if (CJDEBUG) {
1448  printf("ogl_final_close: All done...goodbye!\n");
1449  }
1450 
1451  if (OGL(ifp)->cursor)
1452  XDestroyWindow(OGL(ifp)->dispp, OGL(ifp)->cursor);
1453 
1454  XDestroyWindow(OGL(ifp)->dispp, OGL(ifp)->wind);
1455  XFreeColormap(OGL(ifp)->dispp, OGL(ifp)->xcmap);
1456 
1457  if (SGIL(ifp) != NULL) {
1458  /* free up memory associated with image */
1459  if (SGI(ifp)->mi_shmid != -1) {
1460  /* detach from shared memory */
1461  if (shmdt(ifp->if_mem) == -1) {
1462  fb_log("fb_ogl_close shmdt failed, errno=%d\n",
1463  errno);
1464  return -1;
1465  }
1466  } else {
1467  /* free private memory */
1468  (void)free(ifp->if_mem);
1469  }
1470  /* free state information */
1471  (void)free((char *)SGIL(ifp));
1472  SGIL(ifp) = NULL;
1473  }
1474 
1475  if (OGLL(ifp) != NULL) {
1476  (void)free((char *)OGLL(ifp));
1477  OGLL(ifp) = NULL;
1478  }
1479 
1480  ogl_nwindows--;
1481  return 0;
1482 }
1483 
1484 
1485 HIDDEN int
1486 ogl_flush(fb *ifp)
1487 {
1488  if ((ifp->if_mode & MODE_12MASK) == MODE_12DELAY_WRITES_TILL_FLUSH) {
1489  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
1490  fb_log("Warning, ogl_flush: glXMakeCurrent unsuccessful.\n");
1491  }
1492 
1493  /* Send entire in-memory buffer to the screen, all at once */
1494  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
1495  if (SGI(ifp)->mi_doublebuffer) {
1496  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
1497  } else if (OGL(ifp)->copy_flag) {
1498  backbuffer_to_screen(ifp, -1);
1499  }
1500 
1501  /* unattach context for other threads to use, also flushes */
1502  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
1503  }
1504  XFlush(OGL(ifp)->dispp);
1505  glFlush();
1506  return 0;
1507 }
1508 
1509 
1510 HIDDEN int
1511 fb_ogl_close(fb *ifp)
1512 {
1513 
1514  ogl_flush(ifp);
1515 
1516  /* only the last open window can linger -
1517  * call final_close if not lingering
1518  */
1519  if (ogl_nwindows > 1 ||
1520  (ifp->if_mode & MODE_2MASK) == MODE_2TRANSIENT)
1521  return ogl_final_close(ifp);
1522 
1523  if (CJDEBUG)
1524  printf("fb_ogl_close: remaining open to linger awhile.\n");
1525 
1526  /*
1527  * else:
1528  *
1529  * LINGER mode. Don't return to caller until user mouses "close"
1530  * menu item. This may delay final processing in the calling
1531  * function for some time, but the assumption is that the user
1532  * wishes to compare this image with others.
1533  *
1534  * Since we plan to linger here, long after our invoker expected
1535  * us to be gone, be certain that no file descriptors remain open
1536  * to associate us with pipelines, network connections, etc., that
1537  * were ALREADY ESTABLISHED before the point that fb_open() was
1538  * called.
1539  *
1540  * The simple for i=0..20 loop will not work, because that smashes
1541  * some window-manager files. Therefore, we content ourselves
1542  * with eliminating stdin, in the hopes that this will
1543  * successfully terminate any pipes or network connections.
1544  * Standard error/out may be used to print framebuffer debug
1545  * messages, so they're kept around.
1546  */
1547  fclose(stdin);
1548 
1549  while (0 < OGL(ifp)->alive) {
1550  ogl_do_event(ifp);
1551  }
1552 
1553  return 0;
1554 }
1555 
1556 
1557 int
1558 ogl_close_existing(fb *ifp)
1559 {
1560  if (OGL(ifp)->cursor)
1561  XDestroyWindow(OGL(ifp)->dispp, OGL(ifp)->cursor);
1562 
1563  if (SGIL(ifp) != NULL) {
1564  /* free up memory associated with image */
1565  if (SGI(ifp)->mi_shmid != -1) {
1566  /* detach from shared memory */
1567  if (shmdt(ifp->if_mem) == -1) {
1568  fb_log("fb_ogl_close: shmdt failed, errno=%d\n",
1569  errno);
1570  return -1;
1571  }
1572  } else {
1573  /* free private memory */
1574  (void)free(ifp->if_mem);
1575  }
1576  /* free state information */
1577  (void)free((char *)SGIL(ifp));
1578  SGIL(ifp) = NULL;
1579  }
1580 
1581  if (OGLL(ifp) != NULL) {
1582  (void)free((char *)OGLL(ifp));
1583  OGLL(ifp) = NULL;
1584  }
1585 
1586  return 0;
1587 }
1588 
1589 
1590 /*
1591  * Handle any pending input events
1592  */
1593 HIDDEN int
1594 ogl_poll(fb *ifp)
1595 {
1596  ogl_do_event(ifp);
1597 
1598  if (OGL(ifp)->alive < 0)
1599  return 1;
1600  else
1601  return 0;
1602 }
1603 
1604 
1605 /*
1606  * Free shared memory resources, and close.
1607  */
1608 HIDDEN int
1609 ogl_free(fb *ifp)
1610 {
1611  int ret;
1612 
1613  if (CJDEBUG) printf("entering ogl_free\n");
1614  /* Close the framebuffer */
1615  ret = ogl_final_close(ifp);
1616 
1617  if ((ifp->if_mode & MODE_1MASK) == MODE_1SHARED) {
1618  /* If shared mem, release the shared memory segment */
1619  ogl_zapmem();
1620  }
1621  return ret;
1622 }
1623 
1624 
1625 HIDDEN int
1626 ogl_clear(fb *ifp, unsigned char *pp)
1627 {
1628  struct ogl_pixel bg;
1629  register struct ogl_pixel *oglp;
1630  register int cnt;
1631  register int y;
1632 
1633  if (CJDEBUG) printf("entering ogl_clear\n");
1634 
1635  /* Set clear colors */
1636  if (pp != RGBPIXEL_NULL) {
1637  bg.alpha = 0;
1638  bg.red = (pp)[RED];
1639  bg.green = (pp)[GRN];
1640  bg.blue = (pp)[BLU];
1641  } else {
1642  bg.alpha = 0;
1643  bg.red = 0;
1644  bg.green = 0;
1645  bg.blue = 0;
1646  }
1647 
1648  /* Flood rectangle in shared memory */
1649  for (y = 0; y < ifp->if_height; y++) {
1650  oglp = (struct ogl_pixel *)&ifp->if_mem[
1651  (y*SGI(ifp)->mi_memwidth+0)*sizeof(struct ogl_pixel) ];
1652  for (cnt = ifp->if_width-1; cnt >= 0; cnt--) {
1653  *oglp++ = bg; /* struct copy */
1654  }
1655  }
1656 
1657  if (OGL(ifp)->use_ext_ctrl) {
1658  return 0;
1659  }
1660 
1661  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
1662  fb_log("Warning, ogl_clear: glXMakeCurrent unsuccessful.\n");
1663  }
1664 
1665  if (pp != RGBPIXEL_NULL) {
1666  glClearColor(pp[RED]/255.0, pp[GRN]/255.0, pp[BLU]/255.0, 0.0);
1667  } else {
1668  glClearColor(0, 0, 0, 0);
1669  }
1670 
1671  if (OGL(ifp)->copy_flag) {
1672  /* COPY mode: clear both buffers */
1673  if (OGL(ifp)->front_flag) {
1674  glDrawBuffer(GL_BACK);
1675  glClear(GL_COLOR_BUFFER_BIT);
1676  glDrawBuffer(GL_FRONT);
1677  glClear(GL_COLOR_BUFFER_BIT);
1678  } else {
1679  glDrawBuffer(GL_FRONT);
1680  glClear(GL_COLOR_BUFFER_BIT);
1681  glDrawBuffer(GL_BACK);
1682  glClear(GL_COLOR_BUFFER_BIT);
1683  }
1684  } else {
1685  glClear(GL_COLOR_BUFFER_BIT);
1686  if (SGI(ifp)->mi_doublebuffer) {
1687  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
1688  }
1689  }
1690 
1691  /* unattach context for other threads to use */
1692  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
1693 
1694  return 0;
1695 }
1696 
1697 
1698 HIDDEN int
1699 ogl_view(fb *ifp, int xcenter, int ycenter, int xzoom, int yzoom)
1700 {
1701  struct ogl_clip *clp;
1702 
1703  if (CJDEBUG) printf("entering ogl_view\n");
1704 
1705  if (xzoom < 1) xzoom = 1;
1706  if (yzoom < 1) yzoom = 1;
1707  if (ifp->if_xcenter == xcenter && ifp->if_ycenter == ycenter
1708  && ifp->if_xzoom == xzoom && ifp->if_yzoom == yzoom)
1709  return 0;
1710 
1711  if (xcenter < 0 || xcenter >= ifp->if_width)
1712  return -1;
1713  if (ycenter < 0 || ycenter >= ifp->if_height)
1714  return -1;
1715  if (xzoom >= ifp->if_width || yzoom >= ifp->if_height)
1716  return -1;
1717 
1718  ifp->if_xcenter = xcenter;
1719  ifp->if_ycenter = ycenter;
1720  ifp->if_xzoom = xzoom;
1721  ifp->if_yzoom = yzoom;
1722 
1723  if (ifp->if_xzoom > 1 || ifp->if_yzoom > 1)
1724  ifp->if_zoomflag = 1;
1725  else ifp->if_zoomflag = 0;
1726 
1727 
1728  if (OGL(ifp)->use_ext_ctrl) {
1729  ogl_clipper(ifp);
1730  } else {
1731  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
1732  fb_log("Warning, ogl_view: glXMakeCurrent unsuccessful.\n");
1733  }
1734 
1735  /* Set clipping matrix and zoom level */
1736  glMatrixMode(GL_PROJECTION);
1737  if (OGL(ifp)->copy_flag && !OGL(ifp)->front_flag) {
1738  /* COPY mode - no changes to backbuffer copy - just
1739  * need to update front buffer
1740  */
1741  glPopMatrix();
1742  glDrawBuffer(GL_FRONT);
1743  OGL(ifp)->front_flag = 1;
1744  }
1745  glLoadIdentity();
1746 
1747  ogl_clipper(ifp);
1748  clp = &(OGL(ifp)->clip);
1749  glOrtho(clp->oleft, clp->oright, clp->obottom, clp->otop, -1.0, 1.0);
1750  glPixelZoom((float) ifp->if_xzoom, (float) ifp->if_yzoom);
1751 
1752  if (OGL(ifp)->copy_flag) {
1753  backbuffer_to_screen(ifp, -1);
1754  } else {
1755  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
1756  if (SGI(ifp)->mi_doublebuffer) {
1757  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
1758  }
1759  }
1760  glFlush();
1761 
1762  /* unattach context for other threads to use */
1763  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
1764  }
1765 
1766  return 0;
1767 }
1768 
1769 
1770 HIDDEN int
1771 ogl_getview(fb *ifp, int *xcenter, int *ycenter, int *xzoom, int *yzoom)
1772 {
1773  if (CJDEBUG) printf("entering ogl_getview\n");
1774 
1775  *xcenter = ifp->if_xcenter;
1776  *ycenter = ifp->if_ycenter;
1777  *xzoom = ifp->if_xzoom;
1778  *yzoom = ifp->if_yzoom;
1779 
1780  return 0;
1781 }
1782 
1783 
1784 /* read count pixels into pixelp starting at x, y */
1786 ogl_read(fb *ifp, int x, int y, unsigned char *pixelp, size_t count)
1787 {
1788  size_t n;
1789  size_t scan_count; /* # pix on this scanline */
1790  register unsigned char *cp;
1791  ssize_t ret;
1792  register struct ogl_pixel *oglp;
1793 
1794  if (CJDEBUG) printf("entering ogl_read\n");
1795 
1796  if (x < 0 || x >= ifp->if_width ||
1797  y < 0 || y >= ifp->if_height)
1798  return -1;
1799 
1800  ret = 0;
1801  cp = (unsigned char *)(pixelp);
1802 
1803  while (count) {
1804  if (y >= ifp->if_height)
1805  break;
1806 
1807  if (count >= (size_t)(ifp->if_width-x))
1808  scan_count = ifp->if_width-x;
1809  else
1810  scan_count = count;
1811 
1812  oglp = (struct ogl_pixel *)&ifp->if_mem[
1813  (y*SGI(ifp)->mi_memwidth+x)*sizeof(struct ogl_pixel) ];
1814 
1815  n = scan_count;
1816  while (n) {
1817  cp[RED] = oglp->red;
1818  cp[GRN] = oglp->green;
1819  cp[BLU] = oglp->blue;
1820  oglp++;
1821  cp += 3;
1822  n--;
1823  }
1824  ret += scan_count;
1825  count -= scan_count;
1826  x = 0;
1827  /* Advance upwards */
1828  if (++y >= ifp->if_height)
1829  break;
1830  }
1831  return ret;
1832 }
1833 
1834 
1835 /* write count pixels from pixelp starting at xstart, ystart */
1837 ogl_write(fb *ifp, int xstart, int ystart, const unsigned char *pixelp, size_t count)
1838 {
1839  size_t scan_count; /* # pix on this scanline */
1840  register unsigned char *cp;
1841  ssize_t ret;
1842  int ybase;
1843  size_t pix_count; /* # pixels to send */
1844  register int x;
1845  register int y;
1846 
1847  if (CJDEBUG) printf("entering ogl_write\n");
1848 
1849  /* fast exit cases */
1850  pix_count = count;
1851  if (pix_count == 0)
1852  return 0; /* OK, no pixels transferred */
1853 
1854  x = xstart;
1855  ybase = y = ystart;
1856 
1857  if (x < 0 || x >= ifp->if_width ||
1858  y < 0 || y >= ifp->if_height)
1859  return -1;
1860 
1861  ret = 0;
1862  cp = (unsigned char *)(pixelp);
1863 
1864  while (pix_count) {
1865  size_t n;
1866  register struct ogl_pixel *oglp;
1867 
1868  if (y >= ifp->if_height)
1869  break;
1870 
1871  if (pix_count >= (size_t)(ifp->if_width-x))
1872  scan_count = (size_t)(ifp->if_width-x);
1873  else
1874  scan_count = pix_count;
1875 
1876  oglp = (struct ogl_pixel *)&ifp->if_mem[
1877  (y*SGI(ifp)->mi_memwidth+x)*sizeof(struct ogl_pixel) ];
1878 
1879  n = scan_count;
1880  if ((n & 3) != 0) {
1881  /* This code uses 60% of all CPU time */
1882  while (n) {
1883  /* alpha channel is always zero */
1884  oglp->red = cp[RED];
1885  oglp->green = cp[GRN];
1886  oglp->blue = cp[BLU];
1887  oglp++;
1888  cp += 3;
1889  n--;
1890  }
1891  } else {
1892  while (n) {
1893  /* alpha channel is always zero */
1894  oglp[0].red = cp[RED+0*3];
1895  oglp[0].green = cp[GRN+0*3];
1896  oglp[0].blue = cp[BLU+0*3];
1897  oglp[1].red = cp[RED+1*3];
1898  oglp[1].green = cp[GRN+1*3];
1899  oglp[1].blue = cp[BLU+1*3];
1900  oglp[2].red = cp[RED+2*3];
1901  oglp[2].green = cp[GRN+2*3];
1902  oglp[2].blue = cp[BLU+2*3];
1903  oglp[3].red = cp[RED+3*3];
1904  oglp[3].green = cp[GRN+3*3];
1905  oglp[3].blue = cp[BLU+3*3];
1906  oglp += 4;
1907  cp += 3*4;
1908  n -= 4;
1909  }
1910  }
1911  ret += scan_count;
1912  pix_count -= scan_count;
1913  x = 0;
1914  if (++y >= ifp->if_height)
1915  break;
1916  }
1917 
1918  if ((ifp->if_mode & MODE_12MASK) == MODE_12DELAY_WRITES_TILL_FLUSH)
1919  return ret;
1920 
1921  if (!OGL(ifp)->use_ext_ctrl) {
1922 
1923  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
1924  fb_log("Warning, ogl_write: glXMakeCurrent unsuccessful.\n");
1925  }
1926 
1927  if (xstart + count < (size_t)ifp->if_width) {
1928  ogl_xmit_scanlines(ifp, ybase, 1, xstart, count);
1929  if (SGI(ifp)->mi_doublebuffer) {
1930  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
1931  } else if (OGL(ifp)->copy_flag) {
1932  /* repaint one scanline from backbuffer */
1933  backbuffer_to_screen(ifp, ybase);
1934  }
1935  } else {
1936  /* Normal case -- multi-pixel write */
1937  if (SGI(ifp)->mi_doublebuffer) {
1938  /* refresh whole screen */
1939  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
1940  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
1941  } else {
1942  /* just write rectangle */
1943  ogl_xmit_scanlines(ifp, ybase, y-ybase, 0, ifp->if_width);
1944  if (OGL(ifp)->copy_flag) {
1945  backbuffer_to_screen(ifp, -1);
1946  }
1947  }
1948  }
1949  glFlush();
1950 
1951  /* unattach context for other threads to use */
1952  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
1953  }
1954 
1955  return ret;
1956 
1957 }
1958 
1959 
1960 /*
1961  * The task of this routine is to reformat the pixels into SGI
1962  * internal form, and then arrange to have them sent to the screen
1963  * separately.
1964  */
1965 HIDDEN int
1966 ogl_writerect(fb *ifp, int xmin, int ymin, int width, int height, const unsigned char *pp)
1967 {
1968  register int x;
1969  register int y;
1970  register unsigned char *cp;
1971  register struct ogl_pixel *oglp;
1972 
1973  if (CJDEBUG) printf("entering ogl_writerect\n");
1974 
1975 
1976  if (width <= 0 || height <= 0)
1977  return 0; /* do nothing */
1978  if (xmin < 0 || xmin+width > ifp->if_width ||
1979  ymin < 0 || ymin+height > ifp->if_height)
1980  return -1; /* no can do */
1981 
1982  cp = (unsigned char *)(pp);
1983  for (y = ymin; y < ymin+height; y++) {
1984  oglp = (struct ogl_pixel *)&ifp->if_mem[
1985  (y*SGI(ifp)->mi_memwidth+xmin)*sizeof(struct ogl_pixel) ];
1986  for (x = xmin; x < xmin+width; x++) {
1987  /* alpha channel is always zero */
1988  oglp->red = cp[RED];
1989  oglp->green = cp[GRN];
1990  oglp->blue = cp[BLU];
1991  oglp++;
1992  cp += 3;
1993  }
1994  }
1995 
1996  if ((ifp->if_mode & MODE_12MASK) == MODE_12DELAY_WRITES_TILL_FLUSH)
1997  return width*height;
1998 
1999  if (!OGL(ifp)->use_ext_ctrl) {
2000  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
2001  fb_log("Warning, ogl_writerect: glXMakeCurrent unsuccessful.\n");
2002  }
2003 
2004  if (SGI(ifp)->mi_doublebuffer) {
2005  /* refresh whole screen */
2006  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
2007  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
2008  } else {
2009  /* just write rectangle*/
2010  ogl_xmit_scanlines(ifp, ymin, height, xmin, width);
2011  if (OGL(ifp)->copy_flag) {
2012  backbuffer_to_screen(ifp, -1);
2013  }
2014  }
2015 
2016  /* unattach context for other threads to use */
2017  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
2018  }
2019 
2020  return width*height;
2021 }
2022 
2023 
2024 /*
2025  * The task of this routine is to reformat the pixels into SGI
2026  * internal form, and then arrange to have them sent to the screen
2027  * separately.
2028  */
2029 HIDDEN int
2030 ogl_bwwriterect(fb *ifp, int xmin, int ymin, int width, int height, const unsigned char *pp)
2031 {
2032  register int x;
2033  register int y;
2034  register unsigned char *cp;
2035  register struct ogl_pixel *oglp;
2036 
2037  if (CJDEBUG) printf("entering ogl_bwwriterect\n");
2038 
2039 
2040  if (width <= 0 || height <= 0)
2041  return 0; /* do nothing */
2042  if (xmin < 0 || xmin+width > ifp->if_width ||
2043  ymin < 0 || ymin+height > ifp->if_height)
2044  return -1; /* no can do */
2045 
2046  cp = (unsigned char *)(pp);
2047  for (y = ymin; y < ymin+height; y++) {
2048  oglp = (struct ogl_pixel *)&ifp->if_mem[
2049  (y*SGI(ifp)->mi_memwidth+xmin)*sizeof(struct ogl_pixel) ];
2050  for (x = xmin; x < xmin+width; x++) {
2051  register int val;
2052  /* alpha channel is always zero */
2053  oglp->red = (val = *cp++);
2054  oglp->green = val;
2055  oglp->blue = val;
2056  oglp++;
2057  }
2058  }
2059 
2060  if ((ifp->if_mode & MODE_12MASK) == MODE_12DELAY_WRITES_TILL_FLUSH)
2061  return width*height;
2062 
2063  if (!OGL(ifp)->use_ext_ctrl) {
2064  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
2065  fb_log("Warning, ogl_writerect: glXMakeCurrent unsuccessful.\n");
2066  }
2067 
2068  if (SGI(ifp)->mi_doublebuffer) {
2069  /* refresh whole screen */
2070  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
2071  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
2072  } else {
2073  /* just write rectangle*/
2074  ogl_xmit_scanlines(ifp, ymin, height, xmin, width);
2075  if (OGL(ifp)->copy_flag) {
2076  backbuffer_to_screen(ifp, -1);
2077  }
2078  }
2079 
2080  /* unattach context for other threads to use */
2081  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
2082  }
2083 
2084  return width*height;
2085 }
2086 
2087 
2088 HIDDEN int
2089 ogl_rmap(register fb *ifp, register ColorMap *cmp)
2090 {
2091  register int i;
2092 
2093  if (CJDEBUG) printf("entering ogl_rmap\n");
2094 
2095  /* Just parrot back the stored colormap */
2096  for (i = 0; i < 256; i++) {
2097  cmp->cm_red[i] = CMR(ifp)[i]<<8;
2098  cmp->cm_green[i] = CMG(ifp)[i]<<8;
2099  cmp->cm_blue[i] = CMB(ifp)[i]<<8;
2100  }
2101  return 0;
2102 }
2103 
2104 
2105 HIDDEN int
2106 ogl_wmap(register fb *ifp, register const ColorMap *cmp)
2107 {
2108  register int i;
2109  int prev; /* !0 = previous cmap was non-linear */
2110 
2111  if (CJDEBUG) printf("entering ogl_wmap\n");
2112 
2113  prev = SGI(ifp)->mi_cmap_flag;
2114  if (cmp == COLORMAP_NULL) {
2115  ogl_cminit(ifp);
2116  } else {
2117  for (i = 0; i < 256; i++) {
2118  CMR(ifp)[i] = cmp-> cm_red[i]>>8;
2119  CMG(ifp)[i] = cmp-> cm_green[i]>>8;
2120  CMB(ifp)[i] = cmp-> cm_blue[i]>>8;
2121  }
2122  }
2123  SGI(ifp)->mi_cmap_flag = !is_linear_cmap(ifp);
2124 
2125 
2126  if (!OGL(ifp)->use_ext_ctrl) {
2127  if (OGL(ifp)->soft_cmap_flag) {
2128  /* if current and previous maps are linear, return */
2129  if (SGI(ifp)->mi_cmap_flag == 0 && prev == 0) return 0;
2130 
2131  /* Software color mapping, trigger a repaint */
2132 
2133  if (glXMakeCurrent(OGL(ifp)->dispp, OGL(ifp)->wind, OGL(ifp)->glxc)==False) {
2134  fb_log("Warning, ogl_wmap: glXMakeCurrent unsuccessful.\n");
2135  }
2136 
2137  ogl_xmit_scanlines(ifp, 0, ifp->if_height, 0, ifp->if_width);
2138  if (SGI(ifp)->mi_doublebuffer) {
2139  glXSwapBuffers(OGL(ifp)->dispp, OGL(ifp)->wind);
2140  } else if (OGL(ifp)->copy_flag) {
2141  backbuffer_to_screen(ifp, -1);
2142  }
2143 
2144  /* unattach context for other threads to use, also flushes */
2145  glXMakeCurrent(OGL(ifp)->dispp, None, NULL);
2146  } else {
2147  /* Send color map to hardware */
2148  /* This code has yet to be tested */
2149 
2150  for (i = 0; i < 256; i++) {
2151  color_cell[i].pixel = i;
2152  color_cell[i].red = CMR(ifp)[i];
2153  color_cell[i].green = CMG(ifp)[i];
2154  color_cell[i].blue = CMB(ifp)[i];
2155  color_cell[i].flags = DoRed | DoGreen | DoBlue;
2156  }
2157  XStoreColors(OGL(ifp)->dispp, OGL(ifp)->xcmap, color_cell, 256);
2158  }
2159  }
2160 
2161  return 0;
2162 }
2163 
2164 
2165 HIDDEN int
2166 ogl_help(fb *ifp)
2167 {
2168  struct modeflags *mfp;
2169  XVisualInfo *visual = OGL(ifp)->vip;
2170 
2171  fb_log("Description: %s\n", ifp->if_type);
2172  fb_log("Device: %s\n", ifp->if_name);
2173  fb_log("Max width height: %d %d\n",
2174  ifp->if_max_width,
2175  ifp->if_max_height);
2176  fb_log("Default width height: %d %d\n",
2177  ifp->if_width,
2178  ifp->if_height);
2179  fb_log("Usage: /dev/ogl[option letters]\n");
2180  for (mfp = modeflags; mfp->c != '\0'; mfp++) {
2181  fb_log(" %c %s\n", mfp->c, mfp->help);
2182  }
2183 
2184  fb_log("\nCurrent internal state:\n");
2185  fb_log(" mi_doublebuffer=%d\n", SGI(ifp)->mi_doublebuffer);
2186  fb_log(" mi_cmap_flag=%d\n", SGI(ifp)->mi_cmap_flag);
2187  fb_log(" ogl_nwindows=%d\n", ogl_nwindows);
2188 
2189  fb_log("X11 Visual:\n");
2190 
2191  switch (visual->class) {
2192  case DirectColor:
2193  fb_log("\tDirectColor: Alterable RGB maps, pixel RGB subfield indices\n");
2194  fb_log("\tRGB Masks: 0x%x 0x%x 0x%x\n", visual->red_mask,
2195  visual->green_mask, visual->blue_mask);
2196  break;
2197  case TrueColor:
2198  fb_log("\tTrueColor: Fixed RGB maps, pixel RGB subfield indices\n");
2199  fb_log("\tRGB Masks: 0x%x 0x%x 0x%x\n", visual->red_mask,
2200  visual->green_mask, visual->blue_mask);
2201  break;
2202  case PseudoColor:
2203  fb_log("\tPseudoColor: Alterable RGB maps, single index\n");
2204  break;
2205  case StaticColor:
2206  fb_log("\tStaticColor: Fixed RGB maps, single index\n");
2207  break;
2208  case GrayScale:
2209  fb_log("\tGrayScale: Alterable map (R=G=B), single index\n");
2210  break;
2211  case StaticGray:
2212  fb_log("\tStaticGray: Fixed map (R=G=B), single index\n");
2213  break;
2214  default:
2215  fb_log("\tUnknown visual class %d\n", visual->class);
2216  break;
2217  }
2218  fb_log("\tColormap Size: %d\n", visual->colormap_size);
2219  fb_log("\tBits per RGB: %d\n", visual->bits_per_rgb);
2220  fb_log("\tscreen: %d\n", visual->screen);
2221  fb_log("\tdepth (total bits per pixel): %d\n", visual->depth);
2222  if (visual->depth < 24)
2223  fb_log("\tWARNING: unable to obtain full 24-bits of color, image will be quantized.\n");
2224 
2225  return 0;
2226 }
2227 
2228 
2229 HIDDEN int
2230 ogl_setcursor(fb *ifp, const unsigned char *UNUSED(bits), int UNUSED(xbits), int UNUSED(ybits), int UNUSED(xorig), int UNUSED(yorig))
2231 {
2232  FB_CK_FB(ifp);
2233 
2234  return 0;
2235 }
2236 
2237 
2238 HIDDEN int
2239 ogl_cursor(fb *ifp, int mode, int x, int y)
2240 {
2241  if (mode) {
2242  register int xx, xy;
2243  register int delta;
2244 
2245  /* If we don't have a cursor, create it */
2246  if (!OGL(ifp)->cursor) {
2247  XSetWindowAttributes xswa;
2248  XColor rgb_db_def;
2249  XColor bg, bd;
2250 
2251  XAllocNamedColor(OGL(ifp)->dispp, OGL(ifp)->xcmap, "black",
2252  &rgb_db_def, &bg);
2253  XAllocNamedColor(OGL(ifp)->dispp, OGL(ifp)->xcmap, "white",
2254  &rgb_db_def, &bd);
2255  xswa.background_pixel = bg.pixel;
2256  xswa.border_pixel = bd.pixel;
2257  xswa.colormap = OGL(ifp)->xcmap;
2258  xswa.save_under = True;
2259 
2260  OGL(ifp)->cursor = XCreateWindow(OGL(ifp)->dispp, OGL(ifp)->wind,
2261  0, 0, 4, 4, 2, OGL(ifp)->vip->depth, InputOutput,
2262  OGL(ifp)->vip->visual, CWBackPixel | CWBorderPixel |
2263  CWSaveUnder | CWColormap, &xswa);
2264  }
2265 
2266  delta = ifp->if_width/ifp->if_xzoom/2;
2267  xx = x - (ifp->if_xcenter - delta);
2268  xx *= ifp->if_xzoom;
2269  xx += ifp->if_xzoom/2; /* center cursor */
2270 
2271  delta = ifp->if_height/ifp->if_yzoom/2;
2272  xy = y - (ifp->if_ycenter - delta);
2273  xy *= ifp->if_yzoom;
2274  xy += ifp->if_yzoom/2; /* center cursor */
2275  xy = OGL(ifp)->win_height - xy;
2276 
2277  /* Move cursor into place; make it visible if it isn't */
2278  XMoveWindow(OGL(ifp)->dispp, OGL(ifp)->cursor, xx - 4, xy - 4);
2279 
2280  /* if cursor window is currently not mapped, map it */
2281  if (!ifp->if_cursmode)
2282  XMapRaised(OGL(ifp)->dispp, OGL(ifp)->cursor);
2283  } else {
2284  /* If we have a cursor and it's mapped, unmap it */
2285  if (OGL(ifp)->cursor && ifp->if_cursmode)
2286  XUnmapWindow(OGL(ifp)->dispp, OGL(ifp)->cursor);
2287  }
2288 
2289  /* Without this flush, cursor movement is sluggish */
2290  XFlush(OGL(ifp)->dispp);
2291 
2292  /* Update position of cursor */
2293  ifp->if_cursmode = mode;
2294  ifp->if_xcurs = x;
2295  ifp->if_ycurs = y;
2296 
2297  return 0;
2298 }
2299 
2300 
2301 int
2302 ogl_refresh(fb *ifp, int x, int y, int w, int h)
2303 {
2304  int mm;
2305  struct ogl_clip *clp;
2306 
2307  if (w < 0) {
2308  w = -w;
2309  x -= w;
2310  }
2311 
2312  if (h < 0) {
2313  h = -h;
2314  y -= h;
2315  }
2316 
2317 
2318  glGetIntegerv(GL_MATRIX_MODE, &mm);
2319  glMatrixMode(GL_PROJECTION);
2320  glPushMatrix();
2321  glLoadIdentity();
2322 
2323  ogl_clipper(ifp);
2324  clp = &(OGL(ifp)->clip);
2325  glOrtho(clp->oleft, clp->oright, clp->obottom, clp->otop, -1.0, 1.0);
2326  glPixelZoom((float) ifp->if_xzoom, (float) ifp->if_yzoom);
2327 
2328  glMatrixMode(GL_MODELVIEW);
2329  glPushMatrix();
2330  glLoadIdentity();
2331 
2332  glViewport(0, 0, OGL(ifp)->win_width, OGL(ifp)->win_height);
2333  ogl_xmit_scanlines(ifp, y, h, x, w);
2334  glMatrixMode(GL_PROJECTION);
2335  glPopMatrix();
2336  glMatrixMode(GL_MODELVIEW);
2337  glPopMatrix();
2338  glMatrixMode(mm);
2339 
2340  if (!OGL(ifp)->use_ext_ctrl) {
2341  glFlush();
2342  }
2343 
2344  return 0;
2345 }
2346 
2347 
2348 /* This is the ONLY thing that we normally "export" */
2349 fb ogl_interface =
2350 {
2351  0, /* magic number slot */
2352  FB_OGL_MAGIC,
2353  fb_ogl_open, /* open device */
2354  ogl_open_existing, /* existing device_open */
2355  ogl_close_existing, /* existing device_close */
2356  ogl_get_fbps, /* get platform specific memory */
2357  ogl_put_fbps, /* free platform specific memory */
2358  fb_ogl_close, /* close device */
2359  ogl_clear, /* clear device */
2360  ogl_read, /* read pixels */
2361  ogl_write, /* write pixels */
2362  ogl_rmap, /* read colormap */
2363  ogl_wmap, /* write colormap */
2364  ogl_view, /* set view */
2365  ogl_getview, /* get view */
2366  ogl_setcursor, /* define cursor */
2367  ogl_cursor, /* set cursor */
2368  fb_sim_getcursor, /* get cursor */
2369  fb_sim_readrect, /* read rectangle */
2370  ogl_writerect, /* write rectangle */
2372  ogl_bwwriterect, /* write rectangle */
2373  ogl_configureWindow,
2374  ogl_refresh,
2375  ogl_poll, /* process events */
2376  ogl_flush, /* flush output */
2377  ogl_free, /* free resources */
2378  ogl_help, /* help message */
2379  "Silicon Graphics OpenGL", /* device description */
2380  XMAXSCREEN+1, /* max width */
2381  YMAXSCREEN+1, /* max height */
2382  "/dev/ogl", /* short device name */
2383  512, /* default/current width */
2384  512, /* default/current height */
2385  -1, /* select file desc */
2386  -1, /* file descriptor */
2387  1, 1, /* zoom */
2388  256, 256, /* window center */
2389  0, 0, 0, /* cursor */
2390  PIXEL_NULL, /* page_base */
2391  PIXEL_NULL, /* page_curp */
2392  PIXEL_NULL, /* page_endp */
2393  -1, /* page_no */
2394  0, /* page_dirty */
2395  0L, /* page_curpos */
2396  0L, /* page_pixels */
2397  0, /* debug */
2398  60000000, /* refresh rate (from fbserv, which had 60 seconds as its default... not sure why) */
2399  {0}, /* u1 */
2400  {0}, /* u2 */
2401  {0}, /* u3 */
2402  {0}, /* u4 */
2403  {0}, /* u5 */
2404  {0} /* u6 */
2405 };
2406 
2407 /* Because class is actually used to access a struct
2408  * entry in this file, preserve our redefinition
2409  * of class for the benefit of avoiding C++ name
2410  * collisions until the end of this file */
2411 #undef class
2412 
2413 #else
2414 
2415 /* quell empty-compilation unit warnings */
2416 static const int unused = 0;
2417 
2418 #endif /* IF_OGL */
2419 
2420 /*
2421  * Local Variables:
2422  * mode: C
2423  * tab-width: 8
2424  * indent-tabs-mode: t
2425  * c-file-style: "stroustrup"
2426  * End:
2427  * ex: shiftwidth=4 tabstop=8
2428  */
Definition: db_flip.c:35
ptrdiff_t ssize_t
Definition: common.h:119
#define COLORMAP_NULL
Definition: fb.h:91
#define SHMEM_KEY
Definition: fb_private.h:65
void fb_log(const char *fmt,...) _BU_ATTR_PRINTF12
Definition: fb_log.c:42
void * data
Definition: fb.h:176
unsigned short cm_green[256]
Definition: fb.h:84
#define BU_CKMAG(_ptr, _magic, _str)
Definition: magic.h:233
#define MODE_1MASK
Definition: if_mem.c:55
#define FB_OGL_MAGIC
Definition: magic.h:182
#define RED
Definition: color.h:39
#define PIXEL_NULL
Definition: fb.h:89
int bu_process_id(void)
Definition: process.c:31
long Display
Definition: dm_xvars.h:49
int if_xzoom
zoom factors
Definition: fb_private.h:120
Header file for the BRL-CAD common definitions.
int fb_sim_bwreadrect(fb *ifp, int xmin, int ymin, int _width, int _height, unsigned char *pp)
Definition: fb_rect.c:102
int if_max_width
max device width
Definition: fb_private.h:111
#define BLU
Definition: color.h:41
ustring width
#define HIDDEN
Definition: common.h:86
char * if_type
what "open" calls it
Definition: fb_private.h:110
int if_ycenter
Definition: fb_private.h:123
if(share_geom)
Definition: nmg_mod.c:3829
int bu_strncmp(const char *string1, const char *string2, size_t n)
Definition: str.c:191
COMPLEX data[64]
Definition: fftest.c:34
void * memset(void *s, int c, size_t n)
int if_selfd
select(fd) for input events if >= 0
Definition: fb_private.h:117
#define FB_CK_FB(_p)
Definition: fb.h:100
int fb_sim_getcursor(fb *ifp, int *mode, int *x, int *y)
Definition: fb_util.c:96
long Colormap
Definition: dm_xvars.h:51
#define BU_GET(_ptr, _type)
Definition: malloc.h:201
unsigned short cm_red[256]
Definition: fb.h:83
int if_max_height
max device height
Definition: fb_private.h:112
oldeumate l2 magic
Definition: nmg_mod.c:3843
#define UNUSED(parameter)
Definition: common.h:239
#define BU_PUT(_ptr, _type)
Definition: malloc.h:215
long Window
Definition: dm_xvars.h:50
#define RGBPIXEL_NULL
Definition: fb.h:90
ustring alpha
Definition: fb.h:82
uint32_t magic
Definition: fb.h:176
int if_cursmode
cursor on/off
Definition: fb_private.h:124
int clip(fastf_t *, fastf_t *, fastf_t *, fastf_t *)
Definition: clip.c:66
int bu_shmget(int *shmid, char **shared_memory, int key, size_t size)
Definition: malloc.c:692
#define MODE_2MASK
Definition: if_mem.c:59
int if_xcurs
cursor position
Definition: fb_private.h:125
A frame-buffer IO structure.
Definition: fb_private.h:80
int if_width
current values
Definition: fb_private.h:115
unsigned short cm_blue[256]
Definition: fb.h:85
#define GRN
Definition: color.h:40
HIDDEN const point_t delta
Definition: sh_prj.c:618
int fb_sim_readrect(fb *ifp, int xmin, int ymin, int _width, int _height, unsigned char *pp)
Definition: fb_rect.c:45
char * if_name
what the user called it
Definition: fb_private.h:114
int if_xcenter
pan position
Definition: fb_private.h:122
Definition: joint.h:109