diff --git a/src/stage/ctrl/demo-controller.cpp b/src/stage/ctrl/demo-controller.cpp index e95d56423..841f45020 100644 --- a/src/stage/ctrl/demo-controller.cpp +++ b/src/stage/ctrl/demo-controller.cpp @@ -20,94 +20,73 @@ #include "stage/ctrl/demo-controller.hpp" -#include "stage/display-service.hpp" -#include "lib/error.hpp" -#include "include/logging.h" +#include "steam/engine/worker/tick-service.hpp" +#include "steam/engine/worker/dummy-image-generator.hpp" + +#include namespace stage { namespace ctrl { - namespace error = lumiera::error; - - - - DemoController::DemoController() - : playing_(false) - , viewerHandle_(0) - { - instance = this; ////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level - } - - DemoController::~DemoController() - { - instance = nullptr; ////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level - } - - - DemoController* DemoController::instance; ////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level - - DemoController& - DemoController::get() ////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level - { - if (not instance) - throw error::Logic ("GTK UI is not in running state" - , LERR_(LIFECYCLE)); - - return *instance; + + namespace { + const uint FPS = 4; } + using std::make_unique; + using steam::node::TickService; + using steam::node::DummyImageGenerator; + + + DemoController::DemoController(FrameSink outputSink) + : imageGen_{make_unique(FPS)} + , tick_{} + , output_{std::move (outputSink)} + , playing_{false} + { } + + DemoController::~DemoController() { stop(); } + + + void + DemoController::processFrame() + { + REQUIRE (tick_); + REQUIRE (imageGen_); + + if (playing_) + output_(imageGen_->next()); + else + output_(imageGen_->current()); + } + + void DemoController::play() { - if (playHandle_) - { - playHandle_.play(true); - playing_ = true; - } - else if (viewerHandle_) - try - { - playHandle_ = lumiera::DummyPlayer::facade().start (viewerHandle_); - playing_ = true; - } - catch (lumiera::error::State& err) - { - WARN (stage, "failed to start playback: %s" ,err.what()); - lumiera_error(); - playing_ = false; - } + if (not tick_) + tick_.reset (new TickService{[this]{ processFrame(); }}); + ASSERT (tick_); + tick_->activate (FPS); + playing_ = true; } void DemoController::pause() { - if (playHandle_) - playHandle_.play(false); + if (tick_) + tick_->activate(0); playing_ = false; } void DemoController::stop() { - playHandle_.close(); + tick_.reset(); // blocks for one cycle to join() playing_ = false; } - bool - DemoController::is_playing() - { - return playing_; - } - - - - void - DemoController::useDisplay (LumieraDisplaySlot display) - { - viewerHandle_ = display; - } - }} // namespace stage::ctrl diff --git a/src/stage/ctrl/demo-controller.hpp b/src/stage/ctrl/demo-controller.hpp index ecf4cf586..1b8a993ab 100644 --- a/src/stage/ctrl/demo-controller.hpp +++ b/src/stage/ctrl/demo-controller.hpp @@ -32,49 +32,45 @@ #define DEMO_CONTROLLER_H #include "stage/gtk-base.hpp" -#include "include/dummy-player-facade.h" -#include "include/display-facade.h" #include "lib/nocopy.hpp" +#include +#include +namespace steam { +namespace node { + class TickService; + class DummyImageGenerator; +}} namespace stage { namespace ctrl { + using std::unique_ptr; + using FrameSink = std::function; /** @deprecated we need a durable design for the playback process */ class DemoController : util::NonCopyable { - - volatile bool playing_; - - lumiera::DummyPlayer::Process playHandle_; - - LumieraDisplaySlot viewerHandle_; - - static DemoController* instance; /////////////////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level + unique_ptr imageGen_; + unique_ptr tick_; + FrameSink output_; + bool playing_; public: - - DemoController(); ~DemoController(); + DemoController(FrameSink); + + bool isPlaying() const { return playing_; } - static DemoController& get(); /////////////////////////////////////////////////////////////////////TICKET #1067 shitty workaround to allow disentangling of top-level - void play(); void pause(); void stop(); - bool is_playing(); - - void useDisplay (LumieraDisplaySlot display); - private: - - void on_frame(); - + void processFrame(); }; diff --git a/src/stage/lumiera-light-theme-complement.css b/src/stage/lumiera-light-theme-complement.css index b5e01c229..2ac8b8d71 100644 --- a/src/stage/lumiera-light-theme-complement.css +++ b/src/stage/lumiera-light-theme-complement.css @@ -30,6 +30,12 @@ .idlabel__icon image { padding-left: 0.5ex; } +.videodisplay { + background-color: Black; + min-width: 320px; + min-height: 240px; +} + /* ---------- Styles for special markup ---------- */ diff --git a/src/stage/lumiera.css b/src/stage/lumiera.css index 90c7f3321..03231c5d7 100644 --- a/src/stage/lumiera.css +++ b/src/stage/lumiera.css @@ -277,6 +277,25 @@ class "gtkmm__CustomObject_TimelineHeaderWidget" style:highest "timeline_header_ */ +/* ---------- Styles for Lumiera Widgets ---------- */ + +/* ElementBoxWidget and IDLabel within */ +.idlabel .image-button { + min-width: unset; + min-height: unset; + padding: inherit; +} +.idlabel__icon image { + padding-left: 0.5ex; +} +.videodisplay { + background-color: Black; + min-width: 320px; + min-height: 240px; +} + + + /* ---------- Styles for special markup ---------- */ .indication-flash, diff --git a/src/stage/output/displayer.cpp b/src/stage/output/displayer.cpp index 50f69ba47..54da1fde3 100644 --- a/src/stage/output/displayer.cpp +++ b/src/stage/output/displayer.cpp @@ -28,8 +28,6 @@ #include "stage/gtk-base.hpp" #include "stage/output/displayer.hpp" -#include "stage/output/xv-displayer.hpp" -#include "stage/output/gdkdisplayer.hpp" namespace stage { namespace output { @@ -46,18 +44,6 @@ namespace output { return DISPLAY_NONE; } - int - Displayer::preferredWidth() - { - return imageWidth; - } - - int - Displayer::preferredHeight() - { - return imageHeight; - } - void Displayer::calculateVideoLayout( int widget_width, int widget_height, diff --git a/src/stage/output/displayer.hpp b/src/stage/output/displayer.hpp index 9f7f6b628..380bc85ab 100644 --- a/src/stage/output/displayer.hpp +++ b/src/stage/output/displayer.hpp @@ -27,6 +27,8 @@ #define STAGE_OUTPUT_DISPLAYER_H +#include "lib/nocopy.hpp" + namespace stage { namespace output { @@ -63,27 +65,29 @@ namespace output { * rewrite the two other put methods as required. */ class Displayer + : util::NonCopyable { protected: - int imageWidth; - int imageHeight; + const int videoWidth; + const int videoHeight; public: virtual ~Displayer() { } + Displayer(int w, int h) + : videoWidth{w} + , videoHeight{h} + { } + /** Indicates if this object can be used to render images on the running system. */ virtual bool usable(); - /** Indicates the format required by the abstract put method. */ + /** Indicates the format required by the abstract put method. + * @todo this feature was seemingly never used... can it be relevant? can we handle different formats? + */ virtual DisplayerInput format(); - /** Expected width of input to put. */ - virtual int preferredWidth(); - - /** Expected height of input to put. */ - virtual int preferredHeight(); - /** * Put an image of a given width and height with the expected input * format (as indicated by the format method). diff --git a/src/stage/output/null-displayer.cpp b/src/stage/output/null-displayer.cpp index c1225eab0..0a1ccf888 100644 --- a/src/stage/output/null-displayer.cpp +++ b/src/stage/output/null-displayer.cpp @@ -14,44 +14,29 @@ /** @file null-displayer.hpp ** Passive deactivated video displayer. - ** @deprecated obsolete since GTK-3 ** @todo WIP as of 5/2025 attempt to accommodate to GTK-3 ////////////////////////////////////////////////TICKET #1403 */ #include "stage/gtk-base.hpp" #include "stage/output/null-displayer.hpp" - -#if false ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display -#include -#endif ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display -#include - -using std::cerr; -using std::endl; +#include "lib/format-cout.hpp" namespace stage { namespace output { NullDisplayer::NullDisplayer (Gtk::Widget* drawing_area, - int width, int height) - : drawingArea( drawing_area ) + int width, int height) + : Displayer{width,height} + , drawingArea_{drawing_area} { - REQUIRE (drawing_area != NULL); + REQUIRE (drawing_area); REQUIRE (width > 0); REQUIRE (height > 0); - - imageWidth = width, - imageHeight = height; + cout << "NullDisplayer("<get_width(), - drawingArea->get_height(), - preferredWidth(), preferredHeight(), + drawingArea_->get_width(), + drawingArea_->get_height(), + videoWidth, videoHeight, video_x, video_y, video_width, video_height); - GdkWindow *window = drawingArea->get_window()->gobj(); + GdkWindow *window = drawingArea_->get_window()->gobj(); REQUIRE (window != NULL); - - #if false ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display - GdkGC *gc = gdk_gc_new( window ); - REQUIRE(gc != NULL); - - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8, - preferredWidth(), preferredHeight(), preferredWidth() * 3, NULL, NULL ); - REQUIRE(pixbuf != NULL); - - GdkPixbuf *scaled_image = gdk_pixbuf_scale_simple( pixbuf, video_width, video_height, GDK_INTERP_NEAREST ); - REQUIRE(scaled_image != NULL); - - gdk_draw_pixbuf( window, gc, scaled_image, 0, 0, video_x, video_y, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0 ); - - g_object_unref( scaled_image ); - g_object_unref( pixbuf ); - g_object_unref( gc ); - #endif ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display + cout << "put("< 0); REQUIRE (height > 0); - - imageWidth = width, - imageHeight = height; } bool @@ -64,12 +62,12 @@ namespace output { video_height = 0; calculateVideoLayout( - drawingArea->get_width(), - drawingArea->get_height(), - preferredWidth(), preferredHeight(), + drawingArea_->get_width(), + drawingArea_->get_height(), + videoWidth, videoHeight, video_x, video_y, video_width, video_height); - GdkWindow *window = drawingArea->get_window()->gobj(); + GdkWindow *window = drawingArea_->get_window()->gobj(); REQUIRE (window != NULL); #if false ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display diff --git a/src/stage/output/pixbuf-displayer.hpp b/src/stage/output/pixbuf-displayer.hpp index 27fe89b9c..f1d5f94b0 100644 --- a/src/stage/output/pixbuf-displayer.hpp +++ b/src/stage/output/pixbuf-displayer.hpp @@ -81,7 +81,7 @@ class GdkDisplayer * The widget that video will be drawn into. * @remarks This value must be a valid pointer. */ - Gtk::Widget* drawingArea; + Gtk::Widget* drawingArea_; }; diff --git a/src/stage/output/xv-displayer.cpp b/src/stage/output/xv-displayer.cpp index e568dfadc..96516b992 100644 --- a/src/stage/output/xv-displayer.cpp +++ b/src/stage/output/xv-displayer.cpp @@ -31,19 +31,17 @@ namespace output { XvDisplayer::XvDisplayer(Gtk::Widget *drawing_area, int width, int height) - : gotPort(false) - , drawingArea(drawing_area) - , xvImage(NULL) + : Displayer{width,height} + , gotPort{false} + , drawingArea_{drawing_area} + , xvImage{nullptr} { - REQUIRE(drawing_area != NULL); - REQUIRE(width > 0); - REQUIRE(height > 0); + REQUIRE (drawing_area); + REQUIRE (width > 0); + REQUIRE (height > 0); INFO(stage, "Trying XVideo at %d x %d", width, height); - imageWidth = width; - imageHeight = height; - shmInfo.shmaddr = NULL; Glib::RefPtr area_window = drawing_area->get_window(); @@ -208,7 +206,7 @@ namespace output { XvDisplayer::put (void* const image) { REQUIRE (image != NULL); - REQUIRE (drawingArea != NULL); + REQUIRE (drawingArea_ != NULL); if (xvImage != NULL) { @@ -216,15 +214,15 @@ namespace output { int video_x = 0, video_y = 0, video_width = 0, video_height = 0; calculateVideoLayout( - drawingArea->get_width(), - drawingArea->get_height(), - preferredWidth(), preferredHeight(), + drawingArea_->get_width(), + drawingArea_->get_height(), + videoWidth, videoHeight, video_x, video_y, video_width, video_height ); memcpy (xvImage->data, image, xvImage->data_size); XvShmPutImage (display, grabbedPort, window, gc, xvImage, - 0, 0, preferredWidth(), preferredHeight(), + 0, 0, videoWidth, videoHeight, video_x, video_y, video_width, video_height, false); } } diff --git a/src/stage/output/xv-displayer.hpp b/src/stage/output/xv-displayer.hpp index 86330eb82..01323009f 100644 --- a/src/stage/output/xv-displayer.hpp +++ b/src/stage/output/xv-displayer.hpp @@ -92,7 +92,7 @@ namespace output { * The widget that video will be drawn into. * @remarks This value must be a valid pointer. */ - Gtk::Widget* drawingArea; + Gtk::Widget* drawingArea_; /** * The display that video will be drawn into. diff --git a/src/stage/panel/viewer-panel.cpp b/src/stage/panel/viewer-panel.cpp index 1c7c02f09..50e7c515b 100644 --- a/src/stage/panel/viewer-panel.cpp +++ b/src/stage/panel/viewer-panel.cpp @@ -33,14 +33,12 @@ namespace panel { ViewerPanel::ViewerPanel (workspace::PanelManager& panelManager ,Gdl::DockItem& dockItem) - : Panel(panelManager, dockItem, getTitle(), getStockID()) - , demoPlayback_{} + : Panel{panelManager, dockItem, getTitle(), getStockID()} + , display_{} + , demoPlayback_{[this](void * const buffer){ display_.pushFrame(buffer); }} { //----- Pack in the Widgets -----// pack_start(display_, PACK_EXPAND_WIDGET); - - FrameDestination outputDestination (sigc::mem_fun(this, &ViewerPanel::on_frame)); - demoPlayback_.useDisplay (DisplayService::setUp (outputDestination)); } const char* @@ -55,14 +53,5 @@ namespace panel { return "panel_viewer"; } - void - ViewerPanel::on_frame (void* buffer) - { - Displayer *displayer = display_.getDisplayer(); - REQUIRE(displayer); - - displayer->put(buffer); - } - }}// namespace stage::panel diff --git a/src/stage/panel/viewer-panel.hpp b/src/stage/panel/viewer-panel.hpp index 55243c90e..cb734fd2e 100644 --- a/src/stage/panel/viewer-panel.hpp +++ b/src/stage/panel/viewer-panel.hpp @@ -33,6 +33,7 @@ namespace panel { class ViewerPanel : public Panel { + widget::VideoDisplayWidget display_; ctrl::DemoController demoPlayback_; public: @@ -40,15 +41,6 @@ namespace panel { static const char* getTitle(); static const gchar* getStockID(); - - - protected: - void on_frame(void *buffer); - - protected: - - /** widget to display the video content */ - widget::VideoDisplayWidget display_; }; }}// namespace stage::panel diff --git a/src/stage/style-scheme.cpp b/src/stage/style-scheme.cpp index c7e46d378..a0bbda4a7 100644 --- a/src/stage/style-scheme.cpp +++ b/src/stage/style-scheme.cpp @@ -81,6 +81,7 @@ namespace stage { cuString CLASS_background {"background"}; ///< opaque backdrop + cuString CLASS_videodisplay {"videodisplay"}; Literal ICON_placement {"placement"}; Literal ICON_arrow_hand_menu {"arrow_hand"}; diff --git a/src/stage/style-scheme.hpp b/src/stage/style-scheme.hpp index 1343ac808..54a79ce5b 100644 --- a/src/stage/style-scheme.hpp +++ b/src/stage/style-scheme.hpp @@ -91,6 +91,7 @@ namespace stage { extern cuString CLASS_background; + extern cuString CLASS_videodisplay; extern Literal ICON_placement; extern Literal ICON_arrow_hand_menu; diff --git a/src/stage/widget/video-display-widget.cpp b/src/stage/widget/video-display-widget.cpp index 9b39dd84b..6248d12be 100644 --- a/src/stage/widget/video-display-widget.cpp +++ b/src/stage/widget/video-display-widget.cpp @@ -29,21 +29,29 @@ namespace stage { namespace widget { - VideoDisplayWidget::VideoDisplayWidget() - : displayer_(NULL) - { } - - - VideoDisplayWidget::~VideoDisplayWidget() - { - if (displayer_) delete displayer_; + namespace { + const uint VIDEO_WIDTH = 320; + const uint VIDEO_HEIGHT = 240; ////////////////////////////////////////////////////////////////////////TICKET #1289 : these should not be hard coded, but negotiated with the OutputManager } - Displayer* - VideoDisplayWidget::getDisplayer() const + using std::make_unique; + using stage::output::XvDisplayer; + using stage::output::GdkDisplayer; + using stage::output::NullDisplayer; + + VideoDisplayWidget::VideoDisplayWidget() { - return displayer_; + get_style_context()->add_class (CLASS_background); // Style to ensure an opaque backdrop + get_style_context()->add_class (CLASS_videodisplay); + } + + + void + VideoDisplayWidget::pushFrame (void* const buffer) + { + REQUIRE(displayer_); + displayer_->put(buffer); } @@ -52,39 +60,26 @@ namespace widget { { // invoke base implementation Gtk::Widget::on_realize (); - - // Set colours - //modify_bg (Gtk::STATE_NORMAL, Gdk::Color ("black")); - - if (displayer_) delete displayer_; - displayer_ = createDisplayer (this, 320, 240); - - add_events (Gdk::ALL_EVENTS_MASK); + setupDisplayer (VIDEO_WIDTH, VIDEO_HEIGHT); } - Displayer* - VideoDisplayWidget::createDisplayer (Gtk::Widget *drawingArea, int width, int height) + void + VideoDisplayWidget::setupDisplayer(uint videoWidth, uint videoHeight) { - REQUIRE (drawingArea != NULL); - REQUIRE (width > 0 && height > 0); + REQUIRE (videoWidth > 0); + REQUIRE (videoHeight > 0); + /* + displayer_ = make_unique (this, videoWidth, videoHeight); + if (displayer_->usable()) + return; - Displayer *displayer = NULL; - - displayer = new XvDisplayer (drawingArea, width, height); - if (!displayer->usable()) - { - delete displayer; - displayer = NULL; - } - - if (!displayer) - { - displayer = new GdkDisplayer (drawingArea, width, height); - ///////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display - } - - return displayer; + displayer_ = make_unique (this, videoWidth, videoHeight); + if (displayer_->usable()) + return; + */ + displayer_ = make_unique (this, videoWidth, videoHeight); + ENSURE (displayer_->usable()); } diff --git a/src/stage/widget/video-display-widget.hpp b/src/stage/widget/video-display-widget.hpp index 75cd584fb..daf8c8155 100644 --- a/src/stage/widget/video-display-widget.hpp +++ b/src/stage/widget/video-display-widget.hpp @@ -26,12 +26,14 @@ #include "stage/gtk-base.hpp" #include "stage/output/displayer.hpp" - -using namespace stage::output; ///////////////////////////////////////////////////////////////////////////////TICKET #1071 no wildcard includes please! +#include namespace stage { namespace widget { - + + using stage::output::Displayer; + + /** * @todo the first UI draft included a video displayer widget library implementation, * Unfortunately, this became defunct with the switch to GTK-3. And a fun fact is, @@ -39,24 +41,22 @@ namespace widget { * as to care for video display ourselves. Someone (TM) need to care for this! */ class VideoDisplayWidget - : public Gtk::DrawingArea + : public Gtk::Image { - Displayer* displayer_; + std::unique_ptr displayer_; public: VideoDisplayWidget(); - ~VideoDisplayWidget(); - Displayer* getDisplayer() const; + void pushFrame (void* const); - private: /* ===== Overrides ===== */ + private: virtual void on_realize() override; - private: /* ===== Internals ===== */ - static Displayer* - createDisplayer (Gtk::Widget* drawingArea, int width, int height); + private: + void setupDisplayer(uint videoWidth, uint videoHeight); }; diff --git a/src/steam/engine/worker/tick-service.hpp b/src/steam/engine/worker/tick-service.hpp index 0ce182e38..6fc94af51 100644 --- a/src/steam/engine/worker/tick-service.hpp +++ b/src/steam/engine/worker/tick-service.hpp @@ -71,9 +71,10 @@ namespace node { ~TickService () { timespan_ = 0; - this->join(); - usleep (200000); // additional delay allowing GTK to dispatch the last output + auto res = this->join(); + WARN_IF (res, steam, "Failure in TickService"); + usleep (200000); // additional delay allowing GTK to dispatch the last output INFO (steam, "TickService shutdown."); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 26327788d..031df5396 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -129665,8 +129665,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) bedeutet: im Grunde kann man wild jede Funktion aufrufen

- - + @@ -129704,7 +129703,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129712,11 +129711,13 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + + - - + + + @@ -129729,50 +129730,81 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - + + - - - + + + - + - + - - + + - - + + + + + - - + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + @@ -150890,7 +150922,7 @@ std::cout << tmpl.render({"what", "World"}) << s - + @@ -150923,6 +150955,18 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + + + + @@ -152002,6 +152046,51 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + +

+ ...und zwar, weil GTK portabel ist, und man eigentlich nicht auf die unterliegende Window/Grafik-Technologie zugreifen sollte; mit Wayland gibt es nun sogar unter Linux eine zweite Variante, d.h. man kann nicht mehr sicher sein, daß man überhaupt an ein X-Window oder X-Display gebunden ist. +

+ +
+ +
+ + + + + + + +

+ Interface: dasjenige Widget, +

+

+ das die tatsächliche Realisierung macht +

+

+ (typischerweise ein Gtk::Window) +

+ +
+
+ + + + +
+ + + + + + + + + @@ -153394,7 +153483,7 @@ std::cout << tmpl.render({"what", "World"}) << s

- /* + /*

 * Gadgets are 'next-generation widgets' - they combine a CSS node @@ -154442,6 +154531,67 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + +

+ es ging darum, an die unterliegenden X-Windows ranzukommen, um sie dann auf dem Bidschirm zu positionierenl +

+ + +
+ + + + + +
+
+

+ Window window = GDK_WINDOW_XID (area_window->gobj()); +

+

+ Display* display = GDK_WINDOW_XDISPLAY (area_window->gobj()); +

+

+ +

+
+
+ +
+ + + + + + + +

+ dieser Code ist anscheinend nicht deprecated +

+ + +
+ + + + + + +

+ Das GTK-Projekt führt seit Gtk-3 zunehmen weitere Abstraktionen ein, über die alle relevanten Aufgaben erledigt werden können (selbst low-Level-Aufgaben wie das Mapping von Device-Koordinaten). Siehe Gtk::Native, und von dort Gdk::Surface +

+ +
+
+
+
+
@@ -161649,8 +161799,7 @@ Since then others have made contributions, see the log for the history.
- - + @@ -161729,8 +161878,7 @@ Since then others have made contributions, see the log for the history.
extrem einfach

- - + @@ -161770,8 +161918,7 @@ Since then others have made contributions, see the log for the history.marginal gebrochen

- -
+