Minsky: 3.17.0
renderNativeWindow.cc
Go to the documentation of this file.
1 /*
2  @copyright Steve Keen 2021
3  @author Russell Standish
4  This file is part of Minsky.
5 
6  Minsky is free software: you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  Minsky is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with Minsky. If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 /* We have created a struct `WindowInformation` that stores the `childWindowId` along with other details like display and window attributes. This information is reused across multiple calls to `renderFrame`.
21 
22 The flow for code will be -- when minsky starts, a call to /minsky/canvas/initializeNativeWindow will be made, with parentWindowId (and offsets) as the parameters (creating child window in electron did not work as expected, so we need to work with offsets). Subsequent repaints can be requested with /minsky/canvas/renderFrame
23 
24 As of now, we create the cairo surface with each call to `renderFrame`, though I think the surface can also be reused. I have a placeholder for pointer to cairo::SurfacePtr (not sure we should have pointer to pointer) but it didn't work as expected, so for now I am recreating the surface in `renderFrame`
25 
26 Please especially review the lifecycle (constructors, desctructors and copy constructors) that I have defined in `renderNativeWindow.cc `. I think the WindowInformation object that is destroyed in the destructor for RenderNativeWindow can be reused (perhaps it can be made a static object?). Also - am not sure how to distinguish between destructor for RenderNativeWindow that will be called with each call to load model (or undo/redo as you mentioned), and the final call when minsky is closed.
27  */
28 
29 #include "RESTProcess_base.h"
30 #include "minsky.h"
31 #include "renderNativeWindow.h"
32 #include "windowInformation.h"
33 #include "cairoSurfaceImage.rcd"
34 #include "cairoSurfaceImage.xcd"
35 #include "renderNativeWindow.rcd"
36 #include "renderNativeWindow.xcd"
37 #include "plot.rcd"
38 #include "plot.xcd"
39 
40 #include "minsky_epilogue.h"
41 
42 #include <stdexcept>
43 #include <string>
44 #include <chrono>
45 
46 using namespace std;
47 using namespace ecolab;
48 
49 //#define FPS_PROFILING_ON
50 #ifdef WIN32
51 #include <windows.h>
52 #undef NTDDI_VERSION
53 #define NTDDI_VERSION NTDDI_WINBLUE
54 #include <shellscalingapi.h>
55 #endif
56 
57 namespace minsky
58 {
59  ecolab::cairo::Colour RenderNativeWindow::backgroundColour{0.8,0.8,0.8,1};
60 
61  namespace
62  {
63  // default dummy surface to arrange a callback on requestRedraw
64  class NativeSurface : public cairo::Surface
65  {
67 
68  public:
69  NativeSurface(RenderNativeWindow &r, cairo_surface_t *s = nullptr, int width = -1, int height = -1) : cairo::Surface(s, width, height), renderNativeWindow(r) {}
70  void requestRedraw() override {renderNativeWindow.requestRedraw();}
71  };
72  } // namespace
73 
74  RenderNativeWindow::~RenderNativeWindow()
75  {
76  minsky().nativeWindowsToRedraw.erase(this);
77  }
78 
79  void RenderNativeWindow::renderFrame(const RenderFrameArgs& args)
80  {
81  {
82  const lock_guard lock(drawMutex);
83  m_frameArgs=args;
84  init();
85  winInfoPtr = std::make_shared<WindowInformation>(stoull(args.parentWindowId), args.offsetLeft, args.offsetTop, args.childWidth, args.childHeight, args.scalingFactor, hasScrollBars(), [this](){draw();});
86  surface.reset(new NativeSurface(*this)); // ensure callback on requestRedraw works
87  }
88  draw();
89  }
90 
91  void RenderNativeWindow::destroyFrame() {
92  const lock_guard lock(drawMutex);
93  winInfoPtr.reset();
94  }
95 
96 void macOSXRedraw(RenderNativeWindow& window,const std::shared_ptr<std::lock_guard<std::mutex>>& lock)
97  {
98 #ifdef MAC_OSX_TK
99  if (!window.winInfoPtr.get()) return;
100  window.winInfoPtr->lock=lock;
101  window.winInfoPtr->requestRedraw();
102 #endif
103  }
104 
105 
106  void RenderNativeWindow::requestRedraw()
107  {
108  if (!winInfoPtr.get()) return;
109  minsky().nativeWindowsToRedraw.insert(this);
110  }
111 
112 
113  void RenderNativeWindow::draw()
114  {
115  const lock_guard lock(drawMutex);
116  if (!winInfoPtr.get())
117  {
118  return;
119  }
120 
121 #ifdef FPS_PROFILING_ON
122  unsigned long t0_render_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
123 #endif
124 
125 
126  auto surfaceToDraw = winInfoPtr->getBufferSurface();
127  if (!surfaceToDraw) return;
128  surfaceToDraw.swap(surface);
129 
130  cairo_reset_clip(surface->cairo());
131  const ecolab::cairo::CairoSave cs(surface->cairo());
132  cairo_set_source_rgba(surface->cairo(), backgroundColour.r,backgroundColour.g,backgroundColour.b,backgroundColour.a);
133  cairo_rectangle(surface->cairo(), 0, 0, winInfoPtr->childWidth, winInfoPtr->childHeight);
134  cairo_fill(surface->cairo());
135  cairo_set_source_rgb(surface->cairo(), 0, 0, 0);
136  redraw(0, 0, winInfoPtr->childWidth, winInfoPtr->childHeight);
137 
138 #ifdef FPS_PROFILING_ON
139  unsigned long t1_png_stream_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
140 #endif
141 
142 #ifdef FPS_PROFILING_ON
143  unsigned long t2_window_copy_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
144 #endif
145 
146  surfaceToDraw.swap(surface);
147  winInfoPtr->copyBufferToMain();
148 
149 
150 #ifdef FPS_PROFILING_ON
151  unsigned long t3_render_over = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
152 
153  unsigned long windowCopyTime = t3_render_over - t2_window_copy_start;
154  unsigned long pngStreamWriteTime = t2_window_copy_start - t1_png_stream_start;
155  unsigned long totalTime = t3_render_over - t0_render_start;
156 
157  cout << "Rendering Time (ms): " << totalTime << " (total) | " << windowCopyTime << " (window copy) | " << pngStreamWriteTime << " (png stream overhead) " << endl;
158 #endif
159  }
160 
161 
162  double RenderNativeWindow::scaleFactor()
163  {
164 #ifdef WIN32
165  DEVICE_SCALE_FACTOR scaleFactor;
166  GetScaleFactorForMonitor(MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY), &scaleFactor);
167  return int(scaleFactor)/100.0;
168 #else
169  return 1;
170 #endif
171  }
172 
173 } // namespace minsky
174 
minsky::Minsky minsky
Definition: pyminsky.cc:28
STL namespace.
classdesc::Exclude< std::shared_ptr< WindowInformation > > winInfoPtr
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::RenderNativeWindow)
void macOSXRedraw(RenderNativeWindow &window, const std::shared_ptr< std::lock_guard< std::mutex >> &lock)
NativeSurface(RenderNativeWindow &r, cairo_surface_t *s=nullptr, int width=-1, int height=-1)