diff --git a/model/renderNativeWindow.cc b/model/renderNativeWindow.cc index 873cbd6bf..5d557bb1f 100644 --- a/model/renderNativeWindow.cc +++ b/model/renderNativeWindow.cc @@ -17,33 +17,19 @@ along with Minsky. If not, see . */ +/* 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`. + +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 + +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` + +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. + */ + #include "renderNativeWindow.h" +#include "windowInformation.h" #include "minsky_epilogue.h" -#if defined(CAIRO_HAS_XLIB_SURFACE) && !defined(MAC_OSX_TK) -#include -#include -#include -#endif - -#if defined(CAIRO_HAS_WIN32_SURFACE) && !defined(__CYGWIN__) -#define USE_WIN32_SURFACE -#endif - -#ifdef _WIN32 -#undef Realloc -#include -#include -#ifdef USE_WIN32_SURFACE -#include -#endif -#endif - -#if defined(MAC_OSX_TK) -#include -#include -#include "getContext.h" -#endif #include #include @@ -53,42 +39,27 @@ using namespace ecolab; namespace minsky { -#ifdef USE_WIN32_SURFACE - inline cairo::SurfacePtr nativeWindowSurface(unsigned long window) - {/* TODO */} -#elif defined(MAC_OSX_TK) - inline cairo::SurfacePtr nativeWindowSurface(unsigned long window) - {/* TODO */} -#else - - int throwOnXError(Display*, XErrorEvent* ev) + void RenderNativeWindow::renderFrame(unsigned long parentWindowId, int offsetLeft, int offsetTop, int childWidth, int childHeight) { - char errorMessage[256]; - XGetErrorText(ev->display, ev->error_code, errorMessage, sizeof(errorMessage)); - throw runtime_error(errorMessage); - } - - inline cairo::SurfacePtr nativeWindowSurface(unsigned long window) - { - // ensure errors are thrown, rather than exit() being called - static bool errorHandlingSet=(XSetErrorHandler(throwOnXError), true); - XWindowAttributes wAttr; - auto display=XOpenDisplay(nullptr); - int err=XGetWindowAttributes(display, window, &wAttr); - if (err>1) - throw runtime_error("Invalid window: "+to_string(window)); - cairo::SurfacePtr r(new cairo::Surface(cairo_xlib_surface_create(display,window,wAttr.visual,wAttr.width,wAttr.height),wAttr.width,wAttr.height)); - cairo_surface_set_device_offset(r->surface(), -wAttr.x, -wAttr.y); - return r; - } -#endif - + if (!winInfoPtr) + winInfoPtr=std::make_shared(parentWindowId, offsetLeft, offsetTop, childWidth, childHeight); + + auto tmp = winInfoPtr->getSurface(); + + //auto tmp = createNativeWindowSurface(*winInfoPtr); + + //TODO:: Review if this paint (below 3 lines) is really needed with each frame + cairo_move_to(tmp->cairo(), 0, 0); + cairo_set_source_rgb(tmp->cairo(), 1, 1, 1); + cairo_paint(tmp->cairo()); - void RenderNativeWindow::renderToNativeWindow(unsigned long window) - { - auto tmp=nativeWindowSurface(window); tmp.swap(surface); - redraw(0,0,surface->width(),surface->height()); + redraw(0, 0, surface->width(), surface->height()); tmp.swap(surface); } -} + + void RenderNativeWindow::resizeWindow(int offsetLeft, int offsetTop, int childWidth, int childHeight) + { + // TODO:: To be implemented... need to recreate child window + } +} // namespace minsky diff --git a/model/renderNativeWindow.h b/model/renderNativeWindow.h index 85ca8ed41..979996b66 100644 --- a/model/renderNativeWindow.h +++ b/model/renderNativeWindow.h @@ -23,13 +23,19 @@ #include namespace minsky -{ - class RenderNativeWindow: public ecolab::CairoSurface +{ + class WindowInformation; + class RenderNativeWindow : public ecolab::CairoSurface { - public: - void renderToNativeWindow(unsigned long window); + private: + CLASSDESC_ACCESS(RenderNativeWindow); + std::shared_ptr winInfoPtr; + + public: + void resizeWindow(int offsetLeft, int offsetTop, int childWidth, int childHeight); + void renderFrame(unsigned long parentWindowId, int offsetLeft, int offsetTop, int childWidth, int childHeight); }; -} +} // namespace minsky #include "renderNativeWindow.cd" #endif diff --git a/model/windowInformation.cc b/model/windowInformation.cc new file mode 100644 index 000000000..0963884dc --- /dev/null +++ b/model/windowInformation.cc @@ -0,0 +1,130 @@ +/* + @copyright Steve Keen 2021 + @author Janak Porwal + This file is part of Minsky. + + Minsky is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Minsky is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Minsky. If not, see . +*/ + +#include "windowInformation.h" +#include "minsky_epilogue.h" + +#include +#include + +#if defined(CAIRO_HAS_XLIB_SURFACE) && !defined(MAC_OSX_TK) +#include +#include +#endif + +#if defined(CAIRO_HAS_WIN32_SURFACE) && !defined(__CYGWIN__) +#define USE_WIN32_SURFACE +#endif + +#ifdef _WIN32 +#undef Realloc +#include +#include +#ifdef USE_WIN32_SURFACE +#include +#endif +#endif + +#if defined(MAC_OSX_TK) +#include +#include +#include "getContext.h" +#endif + +using namespace std; +using namespace ecolab; + +namespace minsky +{ + +#ifdef USE_WIN32_SURFACE +#elif defined(MAC_OSX_TK) +#else + int throwOnXError(Display *, XErrorEvent *ev) + { + char errorMessage[256]; + XGetErrorText(ev->display, ev->error_code, errorMessage, sizeof(errorMessage)); + throw runtime_error(errorMessage); + } +#endif + + unsigned long WindowInformation::getChildWindowId() { + return childWindowId; + } + + Display *WindowInformation::getDisplay() { + return display; + } + + WindowInformation::~WindowInformation() { + childSurface.reset(); + XDestroyWindow(display, childWindowId); + } + + ecolab::cairo::SurfacePtr WindowInformation::getSurface() { + return childSurface; + } + + void WindowInformation::createSurface(){ +#ifdef USE_WIN32_SURFACE + { + /* TODO */ + } +#elif defined(MAC_OSX_TK) + + { + /* TODO */ + } +#else + {childSurface.reset(new cairo::Surface(cairo_xlib_surface_create(getDisplay(), getChildWindowId(), wAttr.visual, childWidth, childHeight), childWidth, childHeight)); + cairo_surface_set_device_offset(childSurface->surface(), -wAttr.x, -wAttr.y); +} +#endif + } + + WindowInformation::WindowInformation(unsigned long parentWin, int left, int top, int cWidth, int cHeight) + { + parentWindowId = parentWin; + offsetLeft = left; + offsetTop = top; + + static bool errorHandlingSet = (XSetErrorHandler(throwOnXError), true); + display = XOpenDisplay(nullptr); + int err = XGetWindowAttributes(display, parentWin, &wAttr); + if (err > 1) + throw runtime_error("Invalid window: " + to_string(parentWin)); + + childWidth = wAttr.width - offsetLeft; + childHeight = wAttr.height - offsetTop; + + // TODO:: Take care of scrollbars + + if (cWidth > 0) { + childWidth = min(childWidth, cWidth); + } + + if (cHeight > 0) { + childHeight = min(childHeight, cHeight); + } + + childWindowId = XCreateSimpleWindow(display, parentWin, offsetLeft, offsetTop, childWidth, childHeight, 0, 0, 0); //TODO:: Should we pass visual and attributes at the end? + XMapWindow(display, childWindowId); + createSurface(); + } +} // namespace minsky \ No newline at end of file diff --git a/model/windowInformation.h b/model/windowInformation.h new file mode 100644 index 000000000..fa9b8c784 --- /dev/null +++ b/model/windowInformation.h @@ -0,0 +1,59 @@ +/* + @copyright Steve Keen 2021 + @author Janak Porwal + This file is part of Minsky. + + Minsky is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Minsky is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Minsky. If not, see . +*/ + +#ifndef WINDOW_INFORMATION_H +#define WINDOW_INFORMATION_H + +#include +#include + +namespace minsky +{ + class WindowInformation + { + unsigned long parentWindowId; + unsigned long childWindowId; + + Display* display; // Weak reference, returned by system + ecolab::cairo::SurfacePtr childSurface; + + private: + void createSurface(); + public: + int childWidth; + int childHeight; + int offsetLeft; + int offsetTop; + + XWindowAttributes wAttr; + unsigned long getChildWindowId(); + Display* getDisplay(); + + public: + ~WindowInformation(); + WindowInformation(unsigned long parentWin, int left, int top, int cWidth, int cHeight); + + ecolab::cairo::SurfacePtr getSurface(); + + WindowInformation(const WindowInformation&)=delete; + void operator=(const WindowInformation&)=delete; + }; +} // namespace minsky + +#endif