1 /*
2 * Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #include "X11SurfaceData.h"
27 #include <jni.h>
28
29 #include <X11/extensions/XShm.h>
30 #include <sys/ipc.h>
31 #include <sys/shm.h>
32
33 #include <X11/extensions/Xrender.h>
34 #include <X11/Xlib-xcb.h>
35 #include <xcb/xcbext.h>
36
37 JavaVM *jvm;
38 jobject backendObj;
39 jmethodID releaseSocketMID;
40
41 xcb_connection_t* xcbCon;
42 void* protBufPtr;
43
44 jint shmMajor;
45
46 XImage *ximg;
47 XShmSegmentInfo *shminfo;
48
49 static void returnSocketCB(void *closure)
50 {
51 JNIEnv *env;
52 (*jvm)->AttachCurrentThread(jvm, (void **)&env, NULL);
53 (*env)->CallVoidMethod(env, backendObj, releaseSocketMID);
54 }
55
56 JNIEXPORT jboolean JNICALL
57 Java_sun_java2d_xr_XRBackendDeferred_nativeInit
58 (JNIEnv *env, jobject this, jobject protoBuf) {
59 int status = (*env)->GetJavaVM(env, &jvm);
60 if(status != 0) {
61 return JNI_FALSE;
62 }
63
64 jclass cls = (*env)->GetObjectClass(env, this);
65
66 releaseSocketMID = (*env)->GetMethodID(env, cls, "releaseSocket", "()V");
67 if (releaseSocketMID == NULL) {
68 return JNI_FALSE;
69 }
70
71 jfieldID renderMajorID = (*env)->GetStaticFieldID(env, cls, "RENDER_MAJOR_OPCODE", "I");
72 if (renderMajorID == NULL) {
73 return JNI_FALSE;
74 }
75
76 int major_opcode, first_event, first_error;
77 XQueryExtension(awt_display, "RENDER", &major_opcode, &first_event, &first_error);
78
79 xcbCon = XGetXCBConnection(awt_display);
80 protBufPtr = (*env)->GetDirectBufferAddress(env, protoBuf);
81 backendObj = (jobject) (*env)->NewGlobalRef(env, this);
82 (*env)->SetStaticIntField(env, cls, renderMajorID, (jint) major_opcode);
83
84 return JNI_TRUE;
85 }
86
87 JNIEXPORT void JNICALL
88 Java_sun_java2d_xr_XRBackendDeferred_issueSyncReq
89 (JNIEnv *env, jobject this) {
90 xcb_discard_reply(xcbCon, xcb_get_input_focus(xcbCon).sequence);
91 }
92
93 JNIEXPORT jint JNICALL
94 Java_sun_java2d_xr_XRBackendDeferred_generateXID
95 (JNIEnv *env, jobject this) {
96 return (jint) xcb_generate_id(xcbCon);
97 }
98
99 JNIEXPORT void JNICALL
100 Java_sun_java2d_xr_XRBackendDeferred_forceSocketReturn(JNIEnv *env, jclass cls) {
101 XNoOp(awt_display);
102 }
103
104 JNIEXPORT jlong JNICALL
105 Java_sun_java2d_xr_XRBackendDeferred_takeSocketNative
106 (JNIEnv *env, jobject this, jboolean queueFence) {
107 uint32_t flags = 0;
108 uint64_t sent;
109
110 // A XGetInputFocus event is used to get notified about completion
111 // of XShmPutImage operations, to know when the XServer has finished
112 // accessing a certain SHM area and we can start writing to it again.
113 // However, we can only use xcb's event handling when xcb has taken
114 // the socket, so we actually queue the event of a previous XShmPutImage
115 // only after we request the socket the next time.
116 jlong fenceSeq = -1;
117 if(queueFence) {
118 fenceSeq = xcb_get_input_focus(xcbCon).sequence;
119 }
120
121 xcb_take_socket(xcbCon, &returnSocketCB, &flags, flags, &sent);
122
123 return fenceSeq;
124 }
125
126 JNIEXPORT void JNICALL
127 Java_sun_java2d_xr_XRBackendDeferred_releaseSocketNative
128 (JNIEnv *env, jobject this, jint requestCnt, jint writtenBytes, jint maskPixmap, jlong maskGC, jint maskWidth, jint maskHeight, jint maskScan,
129 jint maskYOffset, jboolean useShmPutImage, jobject directMaskBuffer) {
130 uint8_t getInputFocusReq[4];
131 getInputFocusReq[0] = 43;
132 getInputFocusReq[1] = 0;
133 getInputFocusReq[2] = 1;
134 getInputFocusReq[3] = 0;
135
136 int paddedWidth = maskWidth + (4 - (maskWidth % 4));
137
138 // no mask to upload
139 if(maskWidth == 0) {
140 struct iovec vects[2];
141 vects[0].iov_base = getInputFocusReq;
142 vects[0].iov_len = 4;
143 vects[1].iov_base = protBufPtr;
144 vects[1].iov_len = writtenBytes;
145 xcb_writev(xcbCon, &vects[1], 1, requestCnt + 0);
146 } else if(useShmPutImage)
147 {
148 struct iovec vects[3];
149 vects[0].iov_base = getInputFocusReq;
150 vects[0].iov_len = 4;
151
152 uint8_t shmPutImgReq[40];
153 shmPutImgReq[0] = shmMajor;
154 shmPutImgReq[1] = 3; //shmPutImage
155 *((uint16_t*) &shmPutImgReq[2]) = 10; //req length
156 *((uint32_t*) &shmPutImgReq[4]) = maskPixmap;
157 *((uint32_t*) &shmPutImgReq[8]) = (uint32_t) XGContextFromGC((GC) jlong_to_ptr(maskGC));
158 *((uint16_t*) &shmPutImgReq[12]) = ximg->width;
159 *((uint16_t*) &shmPutImgReq[14]) = ximg->height;
160 *((uint16_t*) &shmPutImgReq[16]) = 0; //srcX
161 *((uint16_t*) &shmPutImgReq[18]) = maskYOffset; //srcY
162 *((uint16_t*) &shmPutImgReq[20]) = paddedWidth; //src_width
163 *((uint16_t*) &shmPutImgReq[22]) = maskHeight; //src_height
164 *((uint16_t*) &shmPutImgReq[24]) = 0; //dstX
165 *((uint16_t*) &shmPutImgReq[26]) = 0; //dstY
166 shmPutImgReq[28] = 8; //Depth
167 shmPutImgReq[29] = 2; //ZImage
168 shmPutImgReq[30] = 0; //SendEvent
169 shmPutImgReq[31] = 0; //Pad
170 *((uint32_t*) &shmPutImgReq[32]) = shminfo->shmseg;
171 *((uint32_t*) &shmPutImgReq[36]) = ximg->data - shminfo->shmaddr;
172
173 //XShmPutImage
174 vects[1].iov_base = shmPutImgReq;
175 vects[1].iov_len = 40;
176
177 vects[2].iov_base = protBufPtr;
178 vects[2].iov_len = writtenBytes;
179 xcb_writev(xcbCon, &vects[1], 2, requestCnt + 1);
180 } else {
181 // mask upload using XPutImage - in case the mask is not
182 // shm capable or there is too little data to upload to
183 // make the extra overhead of shm buffer handling
184 // (socket handoff + event) worthwhile.
185
186 struct iovec* vects = (struct iovec*) malloc(sizeof(struct iovec) * (maskHeight + 3));
187 if(vects == NULL) {
188 return;
189 }
190
191 uint8_t* maskBufferAddr = (*env)->GetDirectBufferAddress(env, directMaskBuffer);
192
193 vects[0].iov_base = getInputFocusReq;
194 vects[0].iov_len = 4;
195
196 //TODO: Include some heuristic to round to buffer width if not too far away
197 // and send the data with a single iovec in this case
198 uint8_t putImgReqHeader8[24];
199 putImgReqHeader8[0] = 72; //XPutImage
200 putImgReqHeader8[1] = 2; //ZImage
201 *((uint16_t*) &putImgReqHeader8[2]) = 6 + (paddedWidth * maskHeight) / 4; //len in 32-bit words
202 *((uint32_t*) &putImgReqHeader8[4]) = maskPixmap;
203 *((uint32_t*) &putImgReqHeader8[8]) = (uint32_t) XGContextFromGC((GC) jlong_to_ptr(maskGC));
204 *((uint16_t*) &putImgReqHeader8[12]) = paddedWidth;
205 *((uint16_t*) &putImgReqHeader8[14]) = maskHeight;
206 *((uint16_t*) &putImgReqHeader8[16]) = 0; //dstX
207 *((uint16_t*) &putImgReqHeader8[18]) = 0; //dstY
208 putImgReqHeader8[20] = 0; //pad
209 putImgReqHeader8[21] = 8; //depth
210 *((uint16_t*) &putImgReqHeader8[22]) = 0; //pad
211
212 //XPutImage Request
213 vects[1].iov_base = putImgReqHeader8;
214 vects[1].iov_len = 24;
215
216 for(int y = 0; y < maskHeight; y++) {
217 vects[y + 2].iov_base = maskBufferAddr + (y * maskScan);
218 vects[y + 2].iov_len = paddedWidth;
219 }
220
221 vects[maskHeight + 2].iov_base = protBufPtr;
222 vects[maskHeight + 2].iov_len = writtenBytes;
223
224 xcb_writev(xcbCon, &vects[1], maskHeight + 2, requestCnt + 1);
225
226 free(vects);
227 }
228 }
229
230 JNIEXPORT jboolean JNICALL
231 Java_sun_java2d_xr_AATileBufMan_pollForTileCompletion
232 (JNIEnv *env, jclass this, jlong fenceSeq) {
233 void* fenceReply;
234
235 if(xcb_poll_for_reply(xcbCon, (unsigned int) fenceSeq, &fenceReply, NULL) > 0) {
236 free(fenceReply);
237 return JNI_TRUE;
238 }
239
240 return JNI_FALSE;
241 }
242
243 JNIEXPORT jobject JNICALL
244 Java_sun_java2d_xr_AATileBufMan_initShmImage
245 (JNIEnv *env, jobject this, jint width, jint height) {
246 ximg = NULL;
247 shminfo = NULL;
248
249 jclass cls = (*env)->GetObjectClass(env, this);
250
251 int first_event, first_error;
252 if(!XQueryExtension(awt_display, "MIT-SHM", &shmMajor, &first_event, &first_error)) {
253 return NULL;
254 }
255
256 jfieldID bufferScanID = (*env)->GetFieldID(env, cls, "shmBufferScan", "I");
257 if (bufferScanID == NULL) {
258 return NULL;
259 }
260
261 shminfo = malloc(sizeof(XShmSegmentInfo));
262 ximg = XShmCreateImage(awt_display, NULL, 8, ZPixmap, NULL, shminfo, width, height);
263
264 if(!ximg) {
265 fprintf(stderr, "XShmCreateImage XShm problems\n");
266 return NULL;
267 }
268
269 if((shminfo->shmid = shmget(IPC_PRIVATE, ximg->bytes_per_line * ximg->height, IPC_CREAT | 0777)) == -1){
270 fprintf(stderr, "shmget XShm problems\n");
271 return NULL;
272 }
273 if((shminfo->shmaddr = ximg->data = shmat(shminfo->shmid, 0, 0)) == (void *)-1){
274 fprintf(stderr, "shmat XShm problems\n");
275 return NULL;
276 }
277 shminfo->readOnly = False;
278 if(!XShmAttach(awt_display, shminfo)){
279 fprintf(stderr, "XShmAttach XShm problems, falling back to to XImage\n");
280 return NULL;
281 }
282
283 (*env)->SetIntField(env, this, bufferScanID, ximg->bytes_per_line);
284
285 return (*env)->NewDirectByteBuffer(env, ximg->data, ximg->bytes_per_line * ximg->height);
286 }
287
288