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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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 <assert.h>
  23. #include <stdlib.h>
  24. #include <FL/x.H>
  25. #include <rfb/LogWriter.h>
  26. #include <rfb/Exception.h>
  27. #include "i18n.h"
  28. #include "X11PixelBuffer.h"
  29. using namespace rfb;
  30. static rfb::LogWriter vlog("X11PixelBuffer");
  31. static PixelFormat display_pf()
  32. {
  33. int i;
  34. int bpp;
  35. int trueColour, bigEndian;
  36. int redShift, greenShift, blueShift;
  37. int redMax, greenMax, blueMax;
  38. int nformats;
  39. XPixmapFormatValues* format;
  40. // Might not be open at this point
  41. fl_open_display();
  42. format = XListPixmapFormats(fl_display, &nformats);
  43. for (i = 0; i < nformats; i++)
  44. if (format[i].depth == fl_visual->depth) break;
  45. if (i == nformats)
  46. throw rfb::Exception(_("Error: display lacks pixmap format for default depth"));
  47. switch (format[i].bits_per_pixel) {
  48. case 8:
  49. case 16:
  50. case 32:
  51. bpp = format[i].bits_per_pixel;
  52. break;
  53. default:
  54. throw rfb::Exception(_("Error: couldn't find suitable pixmap format"));
  55. }
  56. XFree(format);
  57. bigEndian = (ImageByteOrder(fl_display) == MSBFirst);
  58. trueColour = (fl_visual->c_class == TrueColor);
  59. if (!trueColour)
  60. throw rfb::Exception(_("Error: only true colour displays supported"));
  61. vlog.info(_("Using default colormap and visual, %sdepth %d."),
  62. (fl_visual->c_class == TrueColor) ? "TrueColor, " :
  63. ((fl_visual->c_class == PseudoColor) ? "PseudoColor, " : ""),
  64. fl_visual->depth);
  65. redShift = ffs(fl_visual->red_mask) - 1;
  66. greenShift = ffs(fl_visual->green_mask) - 1;
  67. blueShift = ffs(fl_visual->blue_mask) - 1;
  68. redMax = fl_visual->red_mask >> redShift;
  69. greenMax = fl_visual->green_mask >> greenShift;
  70. blueMax = fl_visual->blue_mask >> blueShift;
  71. return PixelFormat(bpp, fl_visual->depth, bigEndian, trueColour,
  72. redMax, greenMax, blueMax,
  73. redShift, greenShift, blueShift);
  74. }
  75. X11PixelBuffer::X11PixelBuffer(int width, int height) :
  76. PlatformPixelBuffer(display_pf(), width, height, NULL, 0),
  77. shminfo(NULL), xim(NULL)
  78. {
  79. // Might not be open at this point
  80. fl_open_display();
  81. if (!setupShm()) {
  82. xim = XCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
  83. ZPixmap, 0, 0, width, height, BitmapPad(fl_display), 0);
  84. assert(xim);
  85. xim->data = (char*)malloc(xim->bytes_per_line * xim->height);
  86. assert(xim->data);
  87. }
  88. data = (rdr::U8*)xim->data;
  89. stride = xim->bytes_per_line / (getPF().bpp/8);
  90. }
  91. X11PixelBuffer::~X11PixelBuffer()
  92. {
  93. if (shminfo) {
  94. vlog.debug("Freeing shared memory XImage");
  95. shmdt(shminfo->shmaddr);
  96. shmctl(shminfo->shmid, IPC_RMID, 0);
  97. delete shminfo;
  98. shminfo = NULL;
  99. }
  100. // XDestroyImage() will free(xim->data) if appropriate
  101. if (xim)
  102. XDestroyImage(xim);
  103. xim = NULL;
  104. }
  105. void X11PixelBuffer::draw(int src_x, int src_y, int x, int y, int w, int h)
  106. {
  107. if (shminfo)
  108. XShmPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h, False);
  109. else
  110. XPutImage(fl_display, fl_window, fl_gc, xim, src_x, src_y, x, y, w, h);
  111. }
  112. static bool caughtError;
  113. static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error)
  114. {
  115. caughtError = true;
  116. return 0;
  117. }
  118. int X11PixelBuffer::setupShm()
  119. {
  120. int major, minor;
  121. Bool pixmaps;
  122. XErrorHandler old_handler;
  123. Status status;
  124. if (!XShmQueryVersion(fl_display, &major, &minor, &pixmaps))
  125. return 0;
  126. shminfo = new XShmSegmentInfo;
  127. xim = XShmCreateImage(fl_display, fl_visual->visual, fl_visual->depth,
  128. ZPixmap, 0, shminfo, width(), height());
  129. if (!xim)
  130. goto free_shminfo;
  131. shminfo->shmid = shmget(IPC_PRIVATE,
  132. xim->bytes_per_line * xim->height,
  133. IPC_CREAT|0777);
  134. if (shminfo->shmid == -1)
  135. goto free_xim;
  136. shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0);
  137. if (shminfo->shmaddr == (char *)-1)
  138. goto free_shm;
  139. shminfo->readOnly = True;
  140. // This is the only way we can detect that shared memory won't work
  141. // (e.g. because we're accessing a remote X11 server)
  142. caughtError = false;
  143. old_handler = XSetErrorHandler(XShmAttachErrorHandler);
  144. XShmAttach(fl_display, shminfo);
  145. XSync(fl_display, False);
  146. XSetErrorHandler(old_handler);
  147. if (caughtError)
  148. goto free_shmaddr;
  149. vlog.debug("Using shared memory XImage");
  150. return 1;
  151. free_shmaddr:
  152. shmdt(shminfo->shmaddr);
  153. free_shm:
  154. shmctl(shminfo->shmid, IPC_RMID, 0);
  155. free_xim:
  156. XDestroyImage(xim);
  157. xim = NULL;
  158. free_shminfo:
  159. delete shminfo;
  160. shminfo = NULL;
  161. return 0;
  162. }