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