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