You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

X11PixelBuffer.cxx 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2011-2014 Pierre Ossman for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. #ifdef HAVE_CONFIG_H
  20. #include <config.h>
  21. #endif
  22. #include <stdlib.h>
  23. #include <FL/x.H>
  24. #include <rfb/LogWriter.h>
  25. #include <rfb/Exception.h>
  26. #include "i18n.h"
  27. #include "X11PixelBuffer.h"
  28. using namespace rfb;
  29. static rfb::LogWriter vlog("X11PixelBuffer");
  30. static PixelFormat display_pf()
  31. {
  32. int i;
  33. int bpp;
  34. int trueColour, bigEndian;
  35. int redShift, greenShift, blueShift;
  36. int redMax, greenMax, blueMax;
  37. int nformats;
  38. XPixmapFormatValues* format;
  39. // Might not be open at this point
  40. fl_open_display();
  41. format = XListPixmapFormats(fl_display, &nformats);
  42. for (i = 0; i < nformats; i++)
  43. if (format[i].depth == fl_visual->depth) break;
  44. if (i == nformats)
  45. // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
  46. // to translate.
  47. throw rfb::Exception(_("Display lacks pixmap format for default depth"));
  48. switch (format[i].bits_per_pixel) {
  49. case 8:
  50. case 16:
  51. case 32:
  52. bpp = format[i].bits_per_pixel;
  53. break;
  54. default:
  55. // TRANSLATORS: "pixmap" is an X11 concept and may not be suitable
  56. // to translate.
  57. throw rfb::Exception(_("Couldn't find suitable pixmap format"));
  58. }
  59. XFree(format);
  60. bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
  61. trueColour = (fl_visual->c_class == TrueColor);
  62. if (!trueColour)
  63. throw rfb::Exception(_("Only true colour displays supported"));
  64. vlog.info(_("Using default colormap and visual, TrueColor, depth %d."),
  65. fl_visual->depth);
  66. redShift = ffs(fl_visual->red_mask) - 1;
  67. greenShift = ffs(fl_visual->green_mask) - 1;
  68. blueShift = ffs(fl_visual->blue_mask) - 1;
  69. redMax = fl_visual->red_mask >> redShift;
  70. greenMax = fl_visual->green_mask >> greenShift;
  71. blueMax = fl_visual->blue_mask >> blueShift;
  72. return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
  73. redMax, greenMax, blueMax,
  74. redShift, greenShift, blueShift);
  75. }
  76. X11PixelBuffer::X11PixelBuffer(int width, int height) :
  77. PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
  78. shminfo(NULL), xim(NULL)
  79. {
  80. // Might not be open at this point
  81. fl_open_display();
  82. if (!setupShm()) {
  83. xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
  84. ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
  85. if (!xim)
  86. throw rfb::Exception(_("Could not create framebuffer image"));
  87. xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
  88. if (!xim->data)
  89. throw rfb::Exception(_("Not enough memory for framebuffer"));
  90. }
  91. data = (rdr::U8*)xim->data;
  92. stride = xim->bytes_per_line / (getPF().bpp/8);
  93. }
  94. X11PixelBuffer::~X11PixelBuffer()
  95. {
  96. if (shminfo) {
  97. vlog.debug("Freeing shared memory XImage");
  98. shmdt(shminfo->shmaddr);
  99. shmctl(shminfo->shmid, IPC_RMID, 0);
  100. delete shminfo;
  101. shminfo = NULL;
  102. }
  103. // XDestroyImage() will free(xim->data) if appropriate
  104. if (xim)
  105. XDestroyImage(xim);
  106. xim = NULL;
  107. }
  108. void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
  109. {
  110. if (shminfo)
  111. XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
  112. else
  113. XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
  114. }
  115. static bool caughtError;
  116. static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
  117. {
  118. caughtError = true;
  119. return 0;
  120. }
  121. int X11PixelBuffer::setupShm()
  122. {
  123. int major, minor;
  124. Bool pixmaps;
  125. XErrorHandler old_handler;
  126. const char *display_name = XDisplayName (NULL);
  127. /* Don't use MIT-SHM on remote displays */
  128. if (*display_name && *display_name != ':')
  129. return 0;
  130. if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
  131. return 0;
  132. shminfo = new XShmSegmentInfo;
  133. xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
  134. ZPixmap, 0, shminfo, width(), height());
  135. if (!xim)
  136. goto free_shminfo;
  137. shminfo->shmid = shmget(IPC_PRIVATE,
  138. xim->bytes_per_line * xim->height,
  139. IPC_CREAT|0777);
  140. if (shminfo->shmid == -1)
  141. goto free_xim;
  142. shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
  143. if (shminfo->shmaddr == (char *)-1)
  144. goto free_shm;
  145. shminfo->readOnly = True;
  146. // This is the only way we can detect that shared memory won't work
  147. // (e.g. because we're accessing a remote X11 server)
  148. caughtError = false;
  149. old_handler = XSetErrorHandler(XShmAttachErrorHandler);
  150. if (!XShmAttach(fl_display, shminfo)) {
  151. XSetErrorHandler(old_handler);
  152. goto free_shmaddr;
  153. }
  154. XSync(fl_display, False);
  155. XSetErrorHandler(old_handler);
  156. if (caughtError)
  157. goto free_shmaddr;
  158. vlog.debug("Using shared memory XImage");
  159. return 1;
  160. free_shmaddr:
  161. shmdt(shminfo->shmaddr);
  162. free_shm:
  163. shmctl(shminfo->shmid, IPC_RMID, 0);
  164. free_xim:
  165. XDestroyImage(xim);
  166. xim = NULL;
  167. free_shminfo:
  168. delete shminfo;
  169. shminfo = NULL;
  170. return 0;
  171. }