Minsky
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 "minsky.h"
30 #include "renderNativeWindow.h"
31 #include "windowInformation.h"
32 #include "cairoSurfaceImage.rcd"
33 #include "cairoSurfaceImage.xcd"
34 #include "renderNativeWindow.rcd"
35 #include "renderNativeWindow.xcd"
36 #include "plot.rcd"
37 #include "plot.xcd"
38 #include "minsky_epilogue.h"
39 
40 #include <stdexcept>
41 #include <string>
42 #include <chrono>
43 
44 using namespace std;
45 using namespace ecolab;
46 
47 //#define FPS_PROFILING_ON
48 #ifdef WIN32
49 #include <windows.h>
50 #undef NTDDI_VERSION
51 #define NTDDI_VERSION NTDDI_WINBLUE
52 #include <shellscalingapi.h>
53 #endif
54 
55 namespace minsky
56 {
57  ecolab::cairo::Colour RenderNativeWindow::backgroundColour{0.8,0.8,0.8,1};
58 
59  namespace
60  {
61  // default dummy surface to arrange a callback on requestRedraw
62  class NativeSurface : public cairo::Surface
63  {
65 
66  public:
67  NativeSurface(RenderNativeWindow &r, cairo_surface_t *s = nullptr, int width = -1, int height = -1) : cairo::Surface(s, width, height), renderNativeWindow(r) {}
68  void requestRedraw() override {renderNativeWindow.requestRedraw();}
69  };
70  } // namespace
71 
72  RenderNativeWindow::~RenderNativeWindow()
73  {
74  minsky().nativeWindowsToRedraw.erase(this);
75  }
76 
77  void RenderNativeWindow::renderFrame(const RenderFrameArgs& args)
78  {
79  {
80  const lock_guard lock(drawMutex);
81  m_frameArgs=args;
82  init();
83  winInfoPtr = std::make_shared<WindowInformation>(stoull(args.parentWindowId), args.offsetLeft, args.offsetTop, args.childWidth, args.childHeight, args.scalingFactor, hasScrollBars(), [this](){draw();});
84  surface.reset(new NativeSurface(*this)); // ensure callback on requestRedraw works
85  }
86  draw();
87  }
88 
89  void RenderNativeWindow::destroyFrame() {
90  const lock_guard lock(drawMutex);
91  winInfoPtr.reset();
92  }
93 
94  void RenderNativeWindow::macOSXRedraw()
95  {
96 #ifdef MAC_OSX_TK
97  if (!winInfoPtr.get()) return;
98  winInfoPtr->requestRedraw();
99 #endif
100  }
101 
102 
103  void RenderNativeWindow::requestRedraw()
104  {
105  if (!winInfoPtr.get()) return;
106  macOSXRedraw();
107  minsky().nativeWindowsToRedraw.insert(this);
108  }
109 
110 
111  void RenderNativeWindow::draw()
112  {
113  const lock_guard lock(drawMutex);
114  if (!winInfoPtr.get())
115  {
116  return;
117  }
118 
119 #ifdef FPS_PROFILING_ON
120  unsigned long t0_render_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
121 #endif
122 
123 
124  auto surfaceToDraw = winInfoPtr->getBufferSurface();
125  if (!surfaceToDraw) return;
126  surfaceToDraw.swap(surface);
127 
128  cairo_reset_clip(surface->cairo());
129  const ecolab::cairo::CairoSave cs(surface->cairo());
130  cairo_set_source_rgba(surface->cairo(), backgroundColour.r,backgroundColour.g,backgroundColour.b,backgroundColour.a);
131  cairo_rectangle(surface->cairo(), 0, 0, winInfoPtr->childWidth, winInfoPtr->childHeight);
132  cairo_fill(surface->cairo());
133  cairo_set_source_rgb(surface->cairo(), 0, 0, 0);
134  redraw(0, 0, winInfoPtr->childWidth, winInfoPtr->childHeight);
135 
136 #ifdef FPS_PROFILING_ON
137  unsigned long t1_png_stream_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
138 #endif
139 
140 #ifdef FPS_PROFILING_ON
141  unsigned long t2_window_copy_start = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
142 #endif
143 
144  surfaceToDraw.swap(surface);
145  winInfoPtr->copyBufferToMain();
146 
147 
148 #ifdef FPS_PROFILING_ON
149  unsigned long t3_render_over = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
150 
151  unsigned long windowCopyTime = t3_render_over - t2_window_copy_start;
152  unsigned long pngStreamWriteTime = t2_window_copy_start - t1_png_stream_start;
153  unsigned long totalTime = t3_render_over - t0_render_start;
154 
155  cout << "Rendering Time (ms): " << totalTime << " (total) | " << windowCopyTime << " (window copy) | " << pngStreamWriteTime << " (png stream overhead) " << endl;
156 #endif
157  }
158 
159 
160  double RenderNativeWindow::scaleFactor()
161  {
162 #ifdef WIN32
163  DEVICE_SCALE_FACTOR scaleFactor;
164  GetScaleFactorForMonitor(MonitorFromPoint(POINT{0,0}, MONITOR_DEFAULTTOPRIMARY), &scaleFactor);
165  return int(scaleFactor)/100.0;
166 #else
167  return 1;
168 #endif
169  }
170 
171 } // namespace minsky
172 
minsky::Minsky minsky
Definition: pyminsky.cc:28
STL namespace.
struct TCLcmd::trap::init_t init
CLASSDESC_ACCESS_EXPLICIT_INSTANTIATION(minsky::RenderNativeWindow)
Creation and access to the minskyTCL_obj object, which has code to record whenever Minsky&#39;s state cha...
Definition: constMap.h:22
NativeSurface(RenderNativeWindow &r, cairo_surface_t *s=nullptr, int width=-1, int height=-1)