1 // Copyright 2016 The SwiftShader Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 #include "FrameBufferX11.hpp" 16 17 #include "libX11.hpp" 18 19 #include <sys/ipc.h> 20 #include <sys/shm.h> 21 #include <string.h> 22 #include <assert.h> 23 24 namespace sw 25 { 26 static int (*PreviousXErrorHandler)(Display *display, XErrorEvent *event) = 0; 27 static bool shmBadAccess = false; 28 29 // Catches BadAcces errors so we can fall back to not using MIT-SHM 30 static int XShmErrorHandler(Display *display, XErrorEvent *event) 31 { 32 if(event->error_code == BadAccess) 33 { 34 shmBadAccess = true; 35 return 0; 36 } 37 else 38 { 39 return PreviousXErrorHandler(display, event); 40 } 41 } 42 43 FrameBufferX11::FrameBufferX11(Display *display, Window window, int width, int height) : FrameBuffer(width, height, false, false), ownX11(!display), x_display(display), x_window(window) 44 { 45 if(!x_display) 46 { 47 x_display = libX11->XOpenDisplay(0); 48 } 49 50 int screen = DefaultScreen(x_display); 51 x_gc = libX11->XDefaultGC(x_display, screen); 52 int depth = libX11->XDefaultDepth(x_display, screen); 53 54 Status status = libX11->XMatchVisualInfo(x_display, screen, 32, TrueColor, &x_visual); 55 bool match = (status != 0 && x_visual.blue_mask == 0xFF); // Prefer X8R8G8B8 56 Visual *visual = match ? x_visual.visual : libX11->XDefaultVisual(x_display, screen); 57 58 mit_shm = (libX11->XShmQueryExtension && libX11->XShmQueryExtension(x_display) == True); 59 60 if(mit_shm) 61 { 62 x_image = libX11->XShmCreateImage(x_display, visual, depth, ZPixmap, 0, &shminfo, width, height); 63 64 shminfo.shmid = shmget(IPC_PRIVATE, x_image->bytes_per_line * x_image->height, IPC_CREAT | SHM_R | SHM_W); 65 shminfo.shmaddr = x_image->data = buffer = (char*)shmat(shminfo.shmid, 0, 0); 66 shminfo.readOnly = False; 67 68 PreviousXErrorHandler = libX11->XSetErrorHandler(XShmErrorHandler); 69 libX11->XShmAttach(x_display, &shminfo); // May produce a BadAccess error 70 libX11->XSync(x_display, False); 71 libX11->XSetErrorHandler(PreviousXErrorHandler); 72 73 if(shmBadAccess) 74 { 75 mit_shm = false; 76 77 XDestroyImage(x_image); 78 shmdt(shminfo.shmaddr); 79 shmctl(shminfo.shmid, IPC_RMID, 0); 80 81 shmBadAccess = false; 82 } 83 } 84 85 if(!mit_shm) 86 { 87 buffer = new char[width * height * 4]; 88 x_image = libX11->XCreateImage(x_display, visual, depth, ZPixmap, 0, buffer, width, height, 32, width * 4); 89 } 90 } 91 92 FrameBufferX11::~FrameBufferX11() 93 { 94 if(!mit_shm) 95 { 96 x_image->data = 0; 97 XDestroyImage(x_image); 98 99 delete[] buffer; 100 buffer = 0; 101 } 102 else 103 { 104 libX11->XShmDetach(x_display, &shminfo); 105 XDestroyImage(x_image); 106 shmdt(shminfo.shmaddr); 107 shmctl(shminfo.shmid, IPC_RMID, 0); 108 } 109 110 if(ownX11) 111 { 112 libX11->XCloseDisplay(x_display); 113 } 114 } 115 116 void *FrameBufferX11::lock() 117 { 118 stride = x_image->bytes_per_line; 119 locked = buffer; 120 121 return locked; 122 } 123 124 void FrameBufferX11::unlock() 125 { 126 locked = nullptr; 127 } 128 129 void FrameBufferX11::blit(void *source, const Rect *sourceRect, const Rect *destRect, Format sourceFormat, size_t sourceStride) 130 { 131 copy(source, sourceFormat, sourceStride); 132 133 if(!mit_shm) 134 { 135 libX11->XPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height); 136 } 137 else 138 { 139 libX11->XShmPutImage(x_display, x_window, x_gc, x_image, 0, 0, 0, 0, width, height, False); 140 } 141 142 libX11->XSync(x_display, False); 143 } 144 } 145 146 NO_SANITIZE_FUNCTION sw::FrameBuffer *createFrameBuffer(void *display, Window window, int width, int height) 147 { 148 return new sw::FrameBufferX11((::Display*)display, window, width, height); 149 } 150