2 * Mesa 3-D graphics library
4 * Copyright (C) 1995-2006 Brian Paul
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * Library for glut using mesa fbdev driver
24 * Written by Sean D'Epagnier (c) 2006
26 * To improve on this library, maybe support subwindows or overlays,
27 * I (sean at depagnier dot com) will do my best to help.
51 #define FBMODES "/etc/fb.modes"
53 struct fb_fix_screeninfo FixedInfo
;
54 struct fb_var_screeninfo VarInfo
;
55 static struct fb_var_screeninfo OrigVarInfo
;
57 static int DesiredDepth
= 0;
59 int FrameBufferFD
= -1;
60 unsigned char *FrameBuffer
;
61 unsigned char *BackBuffer
= NULL
;
64 struct GlutTimer
*GlutTimers
= NULL
;
66 struct timeval StartTime
;
69 GLFBDevContextPtr Context
;
70 GLFBDevBufferPtr Buffer
;
71 GLFBDevVisualPtr Visual
;
78 /* we have to poll to see if we are visible
79 on a framebuffer that is not active */
81 int Swapping
, VTSwitch
;
82 static int FramebufferIndex
;
84 static int Initialized
;
88 /* test if the active console is attached to the same framebuffer */
89 void TestVisible(void) {
90 struct fb_con2fbmap confb
;
93 ioctl(ConsoleFD
, VT_GETSTATE
, &st
);
94 confb
.console
= st
.v_active
;
96 ret
= ioctl(FrameBufferFD
, FBIOGET_CON2FBMAP
, &confb
);
98 if(ret
== -1 || confb
.framebuffer
== FramebufferIndex
) {
105 static void Cleanup(void)
107 /* do not handle this signal when cleaning up */
108 signal(SIGWINCH
, SIG_IGN
);
120 glutDestroyWindow(1);
122 /* restore original variable screen info */
123 if(FrameBufferFD
!= -1) {
124 OrigVarInfo
.xoffset
= 0;
125 OrigVarInfo
.yoffset
= 0;
127 if (ioctl(FrameBufferFD
, FBIOPUT_VSCREENINFO
, &OrigVarInfo
))
128 fprintf(stderr
, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n",
132 munmap(FrameBuffer
, FixedInfo
.smem_len
);
133 close(FrameBufferFD
);
137 /* free allocated back buffer */
138 if(DisplayMode
& GLUT_DOUBLE
)
141 /* free menu items */
145 fprintf(stderr
, "[glfbdev glut] %s", exiterror
);
148 static void CrashHandler(int sig
)
150 sprintf(exiterror
, "Caught signal %d, cleaning up\n", sig
);
154 static void removeArgs(int *argcp
, char **argv
, int num
)
157 for (i
= 0; argv
[i
+num
]; i
++)
158 argv
[i
] = argv
[i
+num
];
164 #define REQPARAM(PARAM) \
165 if (i >= *argcp - 1) { \
166 fprintf(stderr, PARAM" requires a parameter\n"); \
170 void glutInit (int *argcp
, char **argv
)
172 int i
, nomouse
= 0, nokeyboard
= 0, usestdin
= 0;
173 int RequiredWidth
= 0, RequiredHeight
;
180 for (i
= 1; i
< *argcp
;) {
181 if (!strcmp(argv
[i
], "-geometry")) {
182 REQPARAM("geometry");
183 if(sscanf(argv
[i
+1], "%dx%d", &RequiredWidth
,
184 &RequiredHeight
) != 2) {
185 fprintf(stderr
,"Please specify geometry as widthxheight\n");
188 removeArgs(argcp
, &argv
[i
], 2);
190 if (!strcmp(argv
[i
], "-bpp")) {
192 if(sscanf(argv
[i
+1], "%d", &DesiredDepth
) != 1) {
193 fprintf(stderr
, "Please specify a parameter for bpp\n");
196 removeArgs(argcp
, &argv
[i
], 2);
198 if (!strcmp(argv
[i
], "-vt")) {
200 if(sscanf(argv
[i
+1], "%d", &CurrentVT
) != 1) {
201 fprintf(stderr
, "Please specify a parameter for vt\n");
204 removeArgs(argcp
, &argv
[i
], 2);
206 if (!strcmp(argv
[i
], "-mousespeed")) {
207 REQPARAM("mousespeed");
208 if(sscanf(argv
[i
+1], "%lf", &MouseSpeed
) != 1) {
209 fprintf(stderr
, "Please specify a mouse speed, eg: 2.5\n");
212 removeArgs(argcp
, &argv
[i
], 2);
214 if (!strcmp(argv
[i
], "-nomouse")) {
216 removeArgs(argcp
, &argv
[i
], 1);
218 if (!strcmp(argv
[i
], "-nokeyboard")) {
220 removeArgs(argcp
, &argv
[i
], 1);
222 if (!strcmp(argv
[i
], "-stdin")) {
224 removeArgs(argcp
, &argv
[i
], 1);
226 if (!strcmp(argv
[i
], "-gpmmouse")) {
230 fprintf(stderr
, "gpm support not compiled\n");
233 removeArgs(argcp
, &argv
[i
], 1);
235 if (!strcmp(argv
[i
], "--")) {
236 removeArgs(argcp
, &argv
[i
], 1);
242 gettimeofday(&StartTime
, 0);
245 /* set up SIGSEGV to use alternate stack */
247 stack
.ss_size
= SIGSTKSZ
;
248 if(!(stack
.ss_sp
= malloc(SIGSTKSZ
)))
249 sprintf(exiterror
, "Failed to allocate alternate stack for SIGSEGV!\n");
251 sigaltstack(&stack
, NULL
);
253 sa
.sa_handler
= CrashHandler
;
254 sa
.sa_flags
= SA_ONSTACK
;
255 sigemptyset(&sa
.sa_mask
);
256 sigaction(SIGSEGV
, &sa
, NULL
);
258 signal(SIGINT
, CrashHandler
);
259 signal(SIGTERM
, CrashHandler
);
260 signal(SIGABRT
, CrashHandler
);
265 InitializeVT(usestdin
);
267 fbdev
= getenv("FRAMEBUFFER");
270 if(!sscanf(fbdev
, "/dev/fb%d", &FramebufferIndex
))
271 if(!sscanf(fbdev
, "/dev/fb/%d", &FramebufferIndex
))
272 sprintf(exiterror
, "Could not determine Framebuffer index!\n");
276 struct fb_con2fbmap confb
;
277 int fd
= open("/dev/fb0", O_RDWR
);
279 FramebufferIndex
= 0;
281 confb
.console
= CurrentVT
;
282 if(ioctl(fd
, FBIOGET_CON2FBMAP
, &confb
) != -1)
283 FramebufferIndex
= confb
.framebuffer
;
284 sprintf(fb
, "/dev/fb%d", FramebufferIndex
);
289 /* open the framebuffer device */
290 FrameBufferFD
= open(fbdev
, O_RDWR
);
291 if (FrameBufferFD
< 0) {
292 sprintf(exiterror
, "Error opening %s: %s\n", fbdev
, strerror(errno
));
296 /* get the fixed screen info */
297 if (ioctl(FrameBufferFD
, FBIOGET_FSCREENINFO
, &FixedInfo
)) {
298 sprintf(exiterror
, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n",
303 /* get the variable screen info */
304 if (ioctl(FrameBufferFD
, FBIOGET_VSCREENINFO
, &OrigVarInfo
)) {
305 sprintf(exiterror
, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
310 /* operate on a copy */
311 VarInfo
= OrigVarInfo
;
313 /* set the depth, resolution, etc */
315 if(!ParseFBModes(RequiredWidth
, RequiredWidth
, RequiredHeight
,
316 RequiredHeight
, 0, MAX_VSYNC
)) {
317 sprintf(exiterror
, "No mode (%dx%d) found in "FBMODES
"\n",
318 RequiredWidth
, RequiredHeight
);
325 void glutInitDisplayMode (unsigned int mode
)
330 static const char *GetStrVal(const char *p
, int *set
, int min
, int max
)
341 val
= strtol(p
+1, &endptr
, 10);
361 if(val
< min
|| val
> max
) {
362 sprintf(exiterror
, "display string value out of range\n");
371 static void SetAttrib(int val
, int attr
)
376 DisplayMode
&= ~attr
;
379 void glutInitDisplayString(const char *string
)
381 const char *p
= string
;
387 if(memcmp(p
, "acca", 4) == 0) {
388 p
= GetStrVal(p
+4, &AccumSize
, 1, 32);
389 SetAttrib(AccumSize
, GLUT_ACCUM
);
391 if(memcmp(p
, "acc", 3) == 0) {
392 p
= GetStrVal(p
+3, &AccumSize
, 1, 32);
393 SetAttrib(AccumSize
, GLUT_ACCUM
);
395 if(memcmp(p
, "depth", 5) == 0) {
396 p
= GetStrVal(p
+5, &DepthSize
, 12, 32);
397 SetAttrib(DepthSize
, GLUT_DEPTH
);
399 if(memcmp(p
, "double", 6) == 0) {
401 p
= GetStrVal(p
+6, &val
, 0, 1);
402 SetAttrib(val
, GLUT_DOUBLE
);
404 if(memcmp(p
, "index", 5) == 0) {
406 p
= GetStrVal(p
+5, &val
, 0, 1);
407 SetAttrib(val
, GLUT_INDEX
);
409 if(memcmp(p
, "stencil", 7) == 0) {
410 p
= GetStrVal(p
+7, &StencilSize
, 0, 1);
411 SetAttrib(StencilSize
, GLUT_STENCIL
);
413 if(memcmp(p
, "samples", 7) == 0) {
415 p
= GetStrVal(p
+7, &NumSamples
, 0, 16);
416 SetAttrib(NumSamples
, GLUT_MULTISAMPLE
);
418 if(p
= strchr(p
, ' '))
425 void glutInitWindowPosition (int x
, int y
)
429 void glutInitWindowSize (int width
, int height
)
433 static void ProcessTimers(void)
435 while(GlutTimers
&& GlutTimers
->time
<= glutGet(GLUT_ELAPSED_TIME
)) {
436 struct GlutTimer
*timer
= GlutTimers
;
437 GlutTimers
= timer
->next
;
438 timer
->func(timer
->value
);
443 void glutMainLoop(void)
448 ReshapeFunc(VarInfo
.xres
, VarInfo
.yres
);
451 sprintf(exiterror
, "Fatal Error: No Display Function registered\n");
470 VisibilityFunc(Visible
? GLUT_VISIBLE
: GLUT_NOT_VISIBLE
);
477 if(!glFBDevMakeCurrent( Context
, Buffer
, Buffer
)) {
478 sprintf(exiterror
, "Failure to Make Current\n");
485 ReshapeFunc(VarInfo
.xres
, VarInfo
.yres
);
491 if(Visible
&& Redisplay
) {
495 if(!(DisplayMode
& GLUT_DOUBLE
)) {
502 /* we sleep if not receiving redisplays, and
503 the main loop is running faster than 2khz */
506 int time
= glutGet(GLUT_ELAPSED_TIME
);
507 if(time
> lasttime
) {
519 int ParseFBModes(int minw
, int maxw
, int minh
, int maxh
, int minf
, int maxf
)
522 struct fb_var_screeninfo vi
= VarInfo
;
524 FILE *fbmodes
= fopen(FBMODES
, "r");
527 sprintf(exiterror
, "Warning: could not open "FBMODES
"\n");
531 while(fgets(buf
, sizeof buf
, fbmodes
)) {
535 if(!(c
= strstr(buf
, "geometry")))
537 v
= sscanf(c
, "geometry %d %d %d %d %d", &vi
.xres
, &vi
.yres
,
538 &vi
.xres_virtual
, &vi
.yres_virtual
, &bpp
);
543 if(maxw
< vi
.xres
&& minw
> vi
.xres
)
546 if(maxw
< vi
.xres
|| minw
> vi
.xres
)
550 if(maxh
< vi
.yres
&& minh
> vi
.yres
)
553 if(maxh
< vi
.yres
|| minh
> vi
.yres
)
556 fgets(buf
, sizeof buf
, fbmodes
);
557 if(!(c
= strstr(buf
, "timings")))
560 v
= sscanf(c
, "timings %d %d %d %d %d %d %d", &vi
.pixclock
,
561 &vi
.left_margin
, &vi
.right_margin
, &vi
.upper_margin
,
562 &vi
.lower_margin
, &vi
.hsync_len
, &vi
.vsync_len
);
567 freq
= 1E12
/vi
.pixclock
568 /(vi
.left_margin
+ vi
.xres
+ vi
.right_margin
+ vi
.hsync_len
)
569 /(vi
.upper_margin
+ vi
.yres
+ vi
.lower_margin
+ vi
.vsync_len
);
572 if(maxf
< freq
&& minf
> freq
)
575 if(maxf
< freq
|| minf
> freq
)
588 void SetVideoMode(void)
590 /* set new variable screen info */
591 if (ioctl(FrameBufferFD
, FBIOPUT_VSCREENINFO
, &VarInfo
)) {
592 sprintf(exiterror
, "FBIOPUT_VSCREENINFO failed: %s\n", strerror(errno
));
593 strcat(exiterror
, "Perhaps the device does not support the selected mode\n");
597 /* reload the screen info to update rgb bits */
598 if (ioctl(FrameBufferFD
, FBIOGET_VSCREENINFO
, &VarInfo
)) {
599 sprintf(exiterror
, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
604 /* reload the fixed info to update color mode */
605 if (ioctl(FrameBufferFD
, FBIOGET_FSCREENINFO
, &FixedInfo
)) {
606 sprintf(exiterror
, "error: ioctl(FBIOGET_FSCREENINFO) failed: %s\n",
611 if (DesiredDepth
&& DesiredDepth
!= VarInfo
.bits_per_pixel
) {
612 sprintf(exiterror
, "error: Could not set set %d bpp\n", DesiredDepth
);
616 if(DisplayMode
& GLUT_INDEX
&& FixedInfo
.visual
== FB_VISUAL_DIRECTCOLOR
) {
617 sprintf(exiterror
, "error: Could not set 8 bit color mode\n");
621 /* initialize colormap */
625 void CreateBuffer(void)
627 int size
= VarInfo
.xres_virtual
* VarInfo
.yres_virtual
628 * VarInfo
.bits_per_pixel
/ 8;
630 /* mmap the framebuffer into our address space */
632 munmap(FrameBuffer
, FixedInfo
.smem_len
);
633 FrameBuffer
= mmap(0, FixedInfo
.smem_len
, PROT_READ
| PROT_WRITE
,
634 MAP_SHARED
, FrameBufferFD
, 0);
635 if (FrameBuffer
== MAP_FAILED
) {
636 sprintf(exiterror
, "error: unable to mmap framebuffer: %s\n",
641 if(DisplayMode
& GLUT_DOUBLE
) {
643 if(!(BackBuffer
= malloc(size
))) {
644 sprintf(exiterror
, "Failed to allocate double buffer\n");
648 BackBuffer
= FrameBuffer
;
651 glFBDevDestroyBuffer(Buffer
);
653 if(!(Buffer
= glFBDevCreateBuffer( &FixedInfo
, &VarInfo
, Visual
,
654 FrameBuffer
, BackBuffer
, size
))) {
655 sprintf(exiterror
, "Failure to create Buffer\n");
660 void CreateVisual(void)
662 int i
, mask
= DisplayMode
;
664 for(i
=0; i
<sizeof(attribs
)/sizeof(*attribs
) && mask
; i
++) {
665 if(mask
& GLUT_DOUBLE
) {
666 attribs
[i
] = GLFBDEV_DOUBLE_BUFFER
;
667 mask
&= ~GLUT_DOUBLE
;
671 if(mask
& GLUT_INDEX
) {
672 attribs
[i
] = GLFBDEV_COLOR_INDEX
;
677 if(mask
& GLUT_DEPTH
) {
678 attribs
[i
] = GLFBDEV_DEPTH_SIZE
;
679 attribs
[++i
] = DepthSize
;
684 if(mask
& GLUT_STENCIL
) {
685 attribs
[i
] = GLFBDEV_STENCIL_SIZE
;
686 attribs
[++i
] = StencilSize
;
687 mask
&= ~GLUT_STENCIL
;
691 if(mask
& GLUT_ACCUM
) {
692 attribs
[i
] = GLFBDEV_ACCUM_SIZE
;
693 attribs
[++i
] = AccumSize
;
698 if(mask
& GLUT_ALPHA
)
699 if(!(DisplayMode
& GLUT_INDEX
)) {
705 if(mask
& GLUT_MULTISAMPLE
) {
706 attribs
[i
] = GLFBDEV_MULTISAMPLE
;
707 attribs
[++i
] = NumSamples
;
708 mask
&= ~GLUT_MULTISAMPLE
;
712 sprintf(exiterror
, "Invalid mode from glutInitDisplayMode\n");
716 attribs
[i
] = GLFBDEV_NONE
;
718 if(!(Visual
= glFBDevCreateVisual( &FixedInfo
, &VarInfo
, attribs
))) {
719 sprintf(exiterror
, "Failure to create Visual\n");
724 static void SignalWinch(int arg
)
726 /* we can't change bitdepth without destroying the visual */
727 int bits_per_pixel
= VarInfo
.bits_per_pixel
;
728 struct fb_bitfield red
= VarInfo
.red
, green
= VarInfo
.green
,
729 blue
= VarInfo
.blue
, transp
= VarInfo
.transp
;
731 /* get the variable screen info */
732 if (ioctl(FrameBufferFD
, FBIOGET_VSCREENINFO
, &VarInfo
)) {
733 sprintf(exiterror
, "error: ioctl(FBIOGET_VSCREENINFO) failed: %s\n",
738 /* restore bitdepth and color masks only */
739 VarInfo
.bits_per_pixel
= bits_per_pixel
;
741 VarInfo
.green
= green
;
743 VarInfo
.transp
= transp
;
748 int glutCreateWindow (const char *title
)
750 if(Initialized
== 0) {
752 char *argv
[] = {NULL
};
753 glutInit(&argc
, argv
);
759 if(DisplayMode
& GLUT_INDEX
)
760 VarInfo
.bits_per_pixel
= 8;
762 if(VarInfo
.bits_per_pixel
== 8)
763 VarInfo
.bits_per_pixel
= 32;
766 VarInfo
.bits_per_pixel
= DesiredDepth
;
771 VarInfo
.vmode
&= ~FB_VMODE_YWRAP
; /* turn off scrolling */
777 if(!(Context
= glFBDevCreateContext(Visual
, NULL
))) {
778 sprintf(exiterror
, "Failure to create Context\n");
782 if(!glFBDevMakeCurrent( Context
, Buffer
, Buffer
)) {
783 sprintf(exiterror
, "Failure to Make Current\n");
790 glutSetWindowTitle(title
);
792 signal(SIGWINCH
, SignalWinch
);
800 int glutCreateSubWindow(int win
, int x
, int y
, int width
, int height
)
805 void glutSetWindow(int win
)
809 int glutGetWindow(void)
814 void glutDestroyWindow(int win
)
816 glFBDevMakeCurrent( NULL
, NULL
, NULL
);
817 glFBDevDestroyContext(Context
);
818 glFBDevDestroyBuffer(Buffer
);
819 glFBDevDestroyVisual(Visual
);
824 void glutPostRedisplay(void)
829 void glutPostWindowRedisplay(int win
)
834 void glutSwapBuffers(void)
838 if(!(DisplayMode
& GLUT_DOUBLE
))
847 glFBDevSwapBuffers(Buffer
);
851 /* if there was a vt switch while swapping, switch now */
853 if(ioctl(ConsoleFD
, VT_ACTIVATE
, VTSwitch
) < 0)
854 sprintf(exiterror
, "Error switching console\n");
859 void glutPositionWindow(int x
, int y
)
863 void glutReshapeWindow(int width
, int height
)
868 if(!ParseFBModes(width
, width
, height
, height
, 0, MAX_VSYNC
))
871 signal(SIGWINCH
, SIG_IGN
);
874 signal(SIGWINCH
, SignalWinch
);
878 void glutFullScreen(void)
882 void glutPopWindow(void)
886 void glutPushWindow(void)
890 void glutShowWindow(void)
895 void glutHideWindow(void)
900 static void UnIconifyWindow(int sig
)
907 if (ioctl(FrameBufferFD
, FBIOPUT_VSCREENINFO
, &VarInfo
)) {
908 sprintf(exiterror
, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n",
920 void glutIconifyWindow(void)
923 signal(SIGCONT
, UnIconifyWindow
);
924 if (ioctl(FrameBufferFD
, FBIOPUT_VSCREENINFO
, &OrigVarInfo
))
925 fprintf(stderr
, "ioctl(FBIOPUT_VSCREENINFO failed): %s\n",
931 void glutSetWindowTitle(const char *name
)
933 /* escape code to set title in screen */
934 if(getenv("TERM") && memcmp(getenv("TERM"), "screen", 6) == 0)
935 printf("\033k%s\033\\", name
);
938 void glutSetIconTitle(const char *name
)