1 /* Copyright (C)2004 Landmark Graphics Corporation
2  * Copyright (C)2005, 2006 Sun Microsystems, Inc.
3  * Copyright (C)2009, 2014, 2018-2019 D. R. Commander
4  *
5  * This library is free software and may be redistributed and/or modified under
6  * the terms of the wxWindows Library License, Version 3.1 or (at your option)
7  * any later version.  The full license is in the LICENSE.txt file included
8  * with this distribution.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * wxWindows Library License for more details.
14  */
15 
16 /* This library abstracts X Video drawing */
17 #include <string.h>
18 #include <stdlib.h>
19 #include "fbxv.h"
20 #include "x11err.h"
21 
22 
23 static int errorLine = -1;
24 static FILE *warningFile = NULL;
25 
26 
27 static char lastError[1024] = "No error";
28 
29 #define THROW(m) \
30 { \
31 	snprintf(lastError, 1023, "%s", m);  errorLine = __LINE__;  goto finally; \
32 }
33 
34 #define TRY_X11(f) \
35 { \
36 	int __err = 0; \
37 	if((__err = (f)) != Success) \
38 	{ \
39 		snprintf(lastError, 1023, \
40 			"X11 %s Error (window may have disappeared)", x11error(__err)); \
41 		errorLine = __LINE__;  goto finally; \
42 	} \
43 }
44 
45 #define ERRIFNOT(f) \
46 { \
47 	if(!(f)) \
48 	{ \
49 		snprintf(lastError, 1023, "X11 Error (window may have disappeared)"); \
50 		errorLine = __LINE__;  goto finally; \
51 	} \
52 }
53 
54 
55 #ifdef USESHM
56 static unsigned long serial = 0;  static int extok = 1;
57 static XErrorHandler prevHandler = NULL;
58 
59 #ifndef X_ShmAttach
60 #define X_ShmAttach  1
61 #endif
62 
xhandler(Display * dpy,XErrorEvent * e)63 static int xhandler(Display *dpy, XErrorEvent *e)
64 {
65 	if(e->serial == serial && (e->minor_code == X_ShmAttach
66 		&& e->error_code == BadAccess))
67 	{
68 		extok = 0;  return 0;
69 	}
70 	if(prevHandler && prevHandler != xhandler) return prevHandler(dpy, e);
71 	else return 0;
72 }
73 #endif
74 
75 
fbxv_geterrmsg(void)76 char *fbxv_geterrmsg(void)
77 {
78 	return lastError;
79 }
80 
81 
fbxv_geterrline(void)82 int fbxv_geterrline(void)
83 {
84 	return errorLine;
85 }
86 
87 
fbxv_printwarnings(FILE * stream)88 void fbxv_printwarnings(FILE *stream)
89 {
90 	warningFile = stream;
91 }
92 
93 
fbxv_init(fbxv_struct * fb,Display * dpy,Window win,int width_,int height_,int format,int useShm)94 int fbxv_init(fbxv_struct *fb, Display *dpy, Window win, int width_,
95 	int height_, int format, int useShm)
96 {
97 	int width, height, k, shmok = 1, nformats;
98 	unsigned int dummy1, dummy2, dummy3, dummy4, dummy5, i, j, nadaptors = 0;
99 	XWindowAttributes xwa;
100 	XvAdaptorInfo *ai = NULL;
101 	XvImageFormatValues *ifv = NULL;
102 
103 	if(!fb) THROW("Invalid argument");
104 
105 	if(!dpy || !win) THROW("Invalid argument");
106 	ERRIFNOT(XGetWindowAttributes(dpy, win, &xwa));
107 	if(width_ > 0) width = width_;  else width = xwa.width;
108 	if(height_ > 0) height = height_;  else height = xwa.height;
109 	if(fb->dpy == dpy && fb->win == win)
110 	{
111 		if(width == fb->reqwidth && height == fb->reqheight && fb->xvi && fb->xgc
112 			&& fb->xvi->data)
113 			return 0;
114 		else if(fbxv_term(fb) == -1) return -1;
115 	}
116 	memset(fb, 0, sizeof(fbxv_struct));
117 	fb->dpy = dpy;  fb->win = win;
118 	fb->reqwidth = width;  fb->reqheight = height;
119 
120 	if(XvQueryExtension(dpy, &dummy1, &dummy2, &dummy3, &dummy4, &dummy5)
121 		!= Success)
122 		THROW("X Video Extension not available");
123 	if(XvQueryAdaptors(dpy, DefaultRootWindow(dpy), &nadaptors, &ai) != Success)
124 		THROW("Could not query X Video adaptors");
125 	if(nadaptors < 1 || !ai) THROW("No X Video adaptors available");
126 
127 	fb->port = -1;
128 	for(i = 0; i < nadaptors; i++)
129 	{
130 		for(j = ai[i].base_id; j < ai[i].base_id + ai[i].num_ports; j++)
131 		{
132 			nformats = 0;
133 			ifv = XvListImageFormats(dpy, j, &nformats);
134 			if(ifv && nformats > 0)
135 			{
136 				for(k = 0; k < nformats; k++)
137 				{
138 					if(ifv[k].id == format)
139 					{
140 						XFree(ifv);  fb->port = j;
141 						goto found;
142 					}
143 				}
144 			}
145 			XFree(ifv);
146 		}
147 	}
148 	found:
149 	XvFreeAdaptorInfo(ai);  ai = NULL;
150 	if(fb->port == -1)
151 		THROW("The X Video implementation on the 2D X Server does not support the desired pixel format");
152 
153 	#ifdef USESHM
154 	if(useShm && XShmQueryExtension(fb->dpy))
155 	{
156 		static int alreadyWarned = 0;
157 		fb->shminfo.shmid = -1;
158 		if(!(fb->xvi = XvShmCreateImage(dpy, fb->port, format, 0, width, height,
159 			&fb->shminfo)))
160 		{
161 			useShm = 0;  goto noshm;
162 		}
163 		if((fb->shminfo.shmid = shmget(IPC_PRIVATE, fb->xvi->data_size,
164 			IPC_CREAT | 0777)) == -1)
165 		{
166 			useShm = 0;  XFree(fb->xvi);  goto noshm;
167 		}
168 		if((fb->shminfo.shmaddr = fb->xvi->data =
169 			(char *)shmat(fb->shminfo.shmid, 0, 0)) == (char *)-1)
170 		{
171 			useShm = 0;  XFree(fb->xvi);  shmctl(fb->shminfo.shmid, IPC_RMID, 0);
172 			goto noshm;
173 		}
174 		fb->shminfo.readOnly = False;
175 		XLockDisplay(dpy);
176 		XSync(dpy, False);
177 		prevHandler = XSetErrorHandler(xhandler);
178 		extok = 1;
179 		serial = NextRequest(dpy);
180 		XShmAttach(dpy, &fb->shminfo);
181 		XSync(dpy, False);
182 		XSetErrorHandler(prevHandler);
183 		shmok = extok;
184 		if(!alreadyWarned && !shmok && warningFile)
185 		{
186 			fprintf(warningFile,
187 				"[FBX] WARNING: MIT-SHM extension failed to initialize (this is normal on a\n");
188 			fprintf(warningFile, "[FBX]    remote X connection.)\n");
189 			alreadyWarned = 1;
190 		}
191 		XUnlockDisplay(dpy);
192 		shmctl(fb->shminfo.shmid, IPC_RMID, 0);
193 		if(!shmok)
194 		{
195 			useShm = 0;  XFree(fb->xvi);  shmdt(fb->shminfo.shmaddr);
196 			shmctl(fb->shminfo.shmid, IPC_RMID, 0);  goto noshm;
197 		}
198 		fb->xattach = 1;  fb->shm = 1;
199 	}
200 	else if(useShm)
201 	{
202 		static int alreadyWarned = 0;
203 		if(!alreadyWarned && warningFile)
204 		{
205 			fprintf(warningFile, "[FBX] WARNING: MIT-SHM extension not available.\n");
206 			alreadyWarned = 1;
207 		}
208 		useShm = 0;
209 	}
210 	noshm:
211 	if(!useShm)
212 	#endif
213 	{
214 		if(!(fb->xvi = XvCreateImage(dpy, fb->port, format, 0, width, height)))
215 			THROW("Could not create XvImage structure");
216 		if(!(fb->xvi->data = malloc(fb->xvi->data_size)))
217 			THROW("Memory allocation failure");
218 	}
219 	if(!(fb->xgc = XCreateGC(dpy, win, 0, NULL)))
220 		THROW("Could not create X11 graphics context");
221 	return 0;
222 
223 	finally:
224 	fbxv_term(fb);
225 	return -1;
226 }
227 
228 
fbxv_write(fbxv_struct * fb,int srcX_,int srcY_,int srcWidth_,int srcHeight_,int dstX_,int dstY_,int dstWidth,int dstHeight)229 int fbxv_write(fbxv_struct *fb, int srcX_, int srcY_, int srcWidth_,
230 	int srcHeight_, int dstX_, int dstY_, int dstWidth, int dstHeight)
231 {
232 	int srcX, srcY, dstX, dstY, srcWidth, srcHeight;
233 	if(!fb) THROW("Invalid argument");
234 
235 	srcX = srcX_ >= 0 ? srcX_ : 0;
236 	srcY = srcY_ >= 0 ? srcY_ : 0;
237 	srcWidth = srcWidth_ > 0 ? srcWidth_ : fb->xvi->width;
238 	srcHeight = srcHeight_ > 0 ? srcHeight_ : fb->xvi->height;
239 	dstX = dstX_ >= 0 ? dstX_ : 0;
240 	dstY = dstY_ >= 0 ? dstY_ : 0;
241 
242 	if(srcWidth > fb->xvi->width) srcWidth = fb->xvi->width;
243 	if(srcHeight > fb->xvi->height) srcHeight = fb->xvi->height;
244 	if(srcX + srcWidth > fb->xvi->width) srcWidth = fb->xvi->width - srcX;
245 	if(srcY + srcHeight > fb->xvi->height) srcHeight = fb->xvi->height - srcY;
246 
247 	#ifdef USESHM
248 	if(fb->shm)
249 	{
250 		if(!fb->xattach)
251 		{
252 			ERRIFNOT(XShmAttach(fb->dpy, &fb->shminfo));  fb->xattach = 1;
253 		}
254 		TRY_X11(XvShmPutImage(fb->dpy, fb->port, fb->win, fb->xgc, fb->xvi, srcX,
255 			srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight, False));
256 	}
257 	else
258 	#endif
259 
260 	TRY_X11(XvPutImage(fb->dpy, fb->port, fb->win, fb->xgc, fb->xvi, srcX, srcY,
261 		srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight));
262 	XFlush(fb->dpy);
263 	XSync(fb->dpy, False);
264 	return 0;
265 
266 	finally:
267 	return -1;
268 }
269 
270 
fbxv_term(fbxv_struct * fb)271 int fbxv_term(fbxv_struct *fb)
272 {
273 	if(!fb) THROW("Invalid argument");
274 	if(fb->xvi && !fb->shm)
275 	{
276 		free(fb->xvi->data);  fb->xvi->data = NULL;
277 	}
278 
279 	#ifdef USESHM
280 	if(fb->shm)
281 	{
282 		if(fb->xattach)
283 		{
284 			XShmDetach(fb->dpy, &fb->shminfo);  XSync(fb->dpy, False);
285 		}
286 		if(fb->shminfo.shmaddr != NULL) shmdt(fb->shminfo.shmaddr);
287 		if(fb->shminfo.shmid != -1) shmctl(fb->shminfo.shmid, IPC_RMID, 0);
288 	}
289 	#endif
290 
291 	if(fb->xvi) XFree(fb->xvi);
292 	if(fb->xgc) XFreeGC(fb->dpy, fb->xgc);
293 	memset(fb, 0, sizeof(fbxv_struct));
294 	return 0;
295 
296 	finally:
297 	return -1;
298 }
299