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.

randr.cxx 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2017 Pierre Ossman for Cendio AB
  3. * Copyright 2014 Brian P. Hinz
  4. *
  5. * This is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This software is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this software; if not, write to the Free Software
  17. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  18. * USA.
  19. */
  20. #ifdef HAVE_CONFIG_H
  21. #include <config.h>
  22. #endif
  23. #include <stdlib.h>
  24. #include <unixcommon.h>
  25. #include <rfb/screenTypes.h>
  26. #include <rfb/LogWriter.h>
  27. #include <RandrGlue.h>
  28. static rfb::LogWriter vlog("RandR");
  29. rfb::ScreenSet computeScreenLayout(int screenIndex, OutputIdMap *outputIdMap)
  30. {
  31. rfb::ScreenSet layout;
  32. OutputIdMap newIdMap;
  33. for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) {
  34. unsigned int outputId;
  35. int x, y, width, height;
  36. /* Disabled? */
  37. if (!vncRandRIsOutputEnabled(screenIndex, i))
  38. continue;
  39. outputId = vncRandRGetOutputId(screenIndex, i);
  40. /* Known output? */
  41. if (outputIdMap->count(outputId) == 1)
  42. newIdMap[outputId] = (*outputIdMap)[outputId];
  43. else {
  44. rdr::U32 id;
  45. OutputIdMap::const_iterator iter;
  46. while (true) {
  47. id = rand();
  48. for (iter = outputIdMap->begin();iter != outputIdMap->end();++iter) {
  49. if (iter->second == id)
  50. break;
  51. }
  52. if (iter == outputIdMap->end())
  53. break;
  54. }
  55. newIdMap[outputId] = id;
  56. }
  57. vncRandRGetOutputDimensions(screenIndex, i, &x, &y, &width, &height);
  58. layout.add_screen(rfb::Screen(newIdMap[outputId], x, y, width, height, 0));
  59. }
  60. /* Only keep the entries that are currently active */
  61. *outputIdMap = newIdMap;
  62. /*
  63. * Make sure we have something to display. Hopefully it's just temporary
  64. * that we have no active outputs...
  65. */
  66. if (layout.num_screens() == 0)
  67. layout.add_screen(rfb::Screen(0, 0, 0, vncGetScreenWidth(screenIndex),
  68. vncGetScreenHeight(screenIndex), 0));
  69. return layout;
  70. }
  71. unsigned int setScreenLayout(int screenIndex,
  72. int fb_width, int fb_height, const rfb::ScreenSet& layout,
  73. OutputIdMap *outputIdMap)
  74. {
  75. int ret;
  76. int availableOutputs;
  77. // RandR support?
  78. if (vncRandRGetOutputCount(screenIndex) == 0)
  79. return rfb::resultProhibited;
  80. /*
  81. * First check that we don't have any active clone modes. That's just
  82. * too messy to deal with.
  83. */
  84. if (vncRandRHasOutputClones(screenIndex)) {
  85. vlog.error("Clone mode active. Refusing to touch screen layout.");
  86. return rfb::resultInvalid;
  87. }
  88. /* Next count how many useful outputs we have... */
  89. availableOutputs = vncRandRGetAvailableOutputs(screenIndex);
  90. /* Try to create more outputs if needed... (only works on Xvnc) */
  91. if (layout.num_screens() > availableOutputs) {
  92. vlog.debug("Insufficient screens. Need to create %d more.",
  93. layout.num_screens() - availableOutputs);
  94. ret = vncRandRCreateOutputs(screenIndex,
  95. layout.num_screens() - availableOutputs);
  96. if (ret < 0) {
  97. vlog.error("Unable to create more screens, as needed by the new client layout.");
  98. return rfb::resultInvalid;
  99. }
  100. }
  101. /* First we might need to resize the screen */
  102. if ((fb_width != vncGetScreenWidth(screenIndex)) ||
  103. (fb_height != vncGetScreenHeight(screenIndex))) {
  104. ret = vncRandRResizeScreen(screenIndex, fb_width, fb_height);
  105. if (!ret) {
  106. vlog.error("Failed to resize screen to %dx%d", fb_width, fb_height);
  107. return rfb::resultInvalid;
  108. }
  109. }
  110. /* Next, reconfigure all known outputs, and turn off the other ones */
  111. for (int i = 0;i < vncRandRGetOutputCount(screenIndex);i++) {
  112. unsigned int output;
  113. rfb::ScreenSet::const_iterator iter;
  114. output = vncRandRGetOutputId(screenIndex, i);
  115. /* Known? */
  116. if (outputIdMap->count(output) == 0)
  117. continue;
  118. /* Find the corresponding screen... */
  119. for (iter = layout.begin();iter != layout.end();++iter) {
  120. if (iter->id == (*outputIdMap)[output])
  121. break;
  122. }
  123. /* Missing? */
  124. if (iter == layout.end()) {
  125. /* Disable and move on... */
  126. ret = vncRandRDisableOutput(screenIndex, i);
  127. if (!ret) {
  128. char *name = vncRandRGetOutputName(screenIndex, i);
  129. vlog.error("Failed to disable unused output '%s'",
  130. name);
  131. free(name);
  132. return rfb::resultInvalid;
  133. }
  134. outputIdMap->erase(output);
  135. continue;
  136. }
  137. /* Reconfigure new mode and position */
  138. ret = vncRandRReconfigureOutput(screenIndex, i,
  139. iter->dimensions.tl.x,
  140. iter->dimensions.tl.y,
  141. iter->dimensions.width(),
  142. iter->dimensions.height());
  143. if (!ret) {
  144. char *name = vncRandRGetOutputName(screenIndex, i);
  145. vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
  146. name,
  147. iter->dimensions.width(), iter->dimensions.height(),
  148. iter->dimensions.tl.x, iter->dimensions.tl.y);
  149. free(name);
  150. return rfb::resultInvalid;
  151. }
  152. }
  153. /* Finally, allocate new outputs for new screens */
  154. rfb::ScreenSet::const_iterator iter;
  155. for (iter = layout.begin();iter != layout.end();++iter) {
  156. OutputIdMap::const_iterator oi;
  157. unsigned int output;
  158. int i;
  159. /* Does this screen have an output already? */
  160. for (oi = outputIdMap->begin();oi != outputIdMap->end();++oi) {
  161. if (oi->second == iter->id)
  162. break;
  163. }
  164. if (oi != outputIdMap->end())
  165. continue;
  166. /* Find an unused output */
  167. for (i = 0;i < vncRandRGetOutputCount(screenIndex);i++) {
  168. output = vncRandRGetOutputId(screenIndex, i);
  169. /* In use? */
  170. if (outputIdMap->count(output) == 1)
  171. continue;
  172. /* Can it be used? */
  173. if (!vncRandRIsOutputUsable(screenIndex, i))
  174. continue;
  175. break;
  176. }
  177. /* Shouldn't happen */
  178. if (i == vncRandRGetOutputCount(screenIndex))
  179. return rfb::resultInvalid;
  180. /*
  181. * Make sure we already have an entry for this, or
  182. * computeScreenLayout() will think it is a brand new output and
  183. * assign it a random id.
  184. */
  185. (*outputIdMap)[output] = iter->id;
  186. /* Reconfigure new mode and position */
  187. ret = vncRandRReconfigureOutput(screenIndex, i,
  188. iter->dimensions.tl.x,
  189. iter->dimensions.tl.y,
  190. iter->dimensions.width(),
  191. iter->dimensions.height());
  192. if (!ret) {
  193. char *name = vncRandRGetOutputName(screenIndex, i);
  194. vlog.error("Failed to reconfigure output '%s' to %dx%d+%d+%d",
  195. name,
  196. iter->dimensions.width(), iter->dimensions.height(),
  197. iter->dimensions.tl.x, iter->dimensions.tl.y);
  198. free(name);
  199. return rfb::resultInvalid;
  200. }
  201. }
  202. /*
  203. * Update timestamp for when screen layout was last changed.
  204. * This is normally done in the X11 request handlers, which is
  205. * why we have to deal with it manually here.
  206. */
  207. vncRandRUpdateSetTime(screenIndex);
  208. return rfb::resultSuccess;
  209. }