Revision: 75630
          http://sourceforge.net/p/brlcad/code/75630
Author:   starseeker
Date:     2020-04-29 16:02:41 +0000 (Wed, 29 Apr 2020)
Log Message:
-----------
Merge changes from trunk through r75629

Modified Paths:
--------------
    brlcad/branches/dm-fb-merge/src/fb/CMakeLists.txt
    brlcad/branches/dm-fb-merge/src/libdm/CMakeLists.txt
    brlcad/branches/dm-fb-merge/src/libdm/tests/CMakeLists.txt
    brlcad/branches/dm-fb-merge/src/libged/typein.c

Added Paths:
-----------
    brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_img.cpp
    brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_tevent.cpp

Removed Paths:
-------------
    brlcad/branches/dm-fb-merge/src/fb/tests/

Property Changed:
----------------
    brlcad/branches/dm-fb-merge/

Index: brlcad/branches/dm-fb-merge
===================================================================
--- brlcad/branches/dm-fb-merge 2020-04-29 15:00:45 UTC (rev 75629)
+++ brlcad/branches/dm-fb-merge 2020-04-29 16:02:41 UTC (rev 75630)

Property changes on: brlcad/branches/dm-fb-merge
___________________________________________________________________
Modified: svn:mergeinfo
## -7,4 +7,4 ##
 /brlcad/branches/osg:62110-62113
 /brlcad/branches/prep-cache:68236-68933
 /brlcad/branches/tcltk86:68300-75257
-/brlcad/trunk:75440-75597
\ No newline at end of property
+/brlcad/trunk:75440-75629
\ No newline at end of property
Modified: brlcad/branches/dm-fb-merge/src/fb/CMakeLists.txt
===================================================================
--- brlcad/branches/dm-fb-merge/src/fb/CMakeLists.txt   2020-04-29 15:00:45 UTC 
(rev 75629)
+++ brlcad/branches/dm-fb-merge/src/fb/CMakeLists.txt   2020-04-29 16:02:41 UTC 
(rev 75630)
@@ -48,8 +48,6 @@
 
 BRLCAD_ADDEXEC(spm-fb spm-fb.c "libdm;libbn")
 
-add_subdirectory(tests)
-
 CMAKEFILES(CMakeLists.txt)
 
 # Local Variables:

Modified: brlcad/branches/dm-fb-merge/src/libdm/CMakeLists.txt
===================================================================
--- brlcad/branches/dm-fb-merge/src/libdm/CMakeLists.txt        2020-04-29 
15:00:45 UTC (rev 75629)
+++ brlcad/branches/dm-fb-merge/src/libdm/CMakeLists.txt        2020-04-29 
16:02:41 UTC (rev 75630)
@@ -101,6 +101,9 @@
 
 add_subdirectory(tests)
 
+
+add_subdirectory(tests)
+
 set(libdm_ignore_files
   CMakeLists.txt
   README

Modified: brlcad/branches/dm-fb-merge/src/libdm/tests/CMakeLists.txt
===================================================================
--- brlcad/branches/dm-fb-merge/src/libdm/tests/CMakeLists.txt  2020-04-29 
15:00:45 UTC (rev 75629)
+++ brlcad/branches/dm-fb-merge/src/libdm/tests/CMakeLists.txt  2020-04-29 
16:02:41 UTC (rev 75630)
@@ -37,7 +37,14 @@
 
 BRLCAD_ADDEXEC(dm_test dm_test.c "libdm;libbu" TEST)
 
+if (BRLCAD_ENABLE_TK)
+  BRLCAD_ADDEXEC(tcl_img tcl_img.cpp 
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
+  BRLCAD_ADDEXEC(tcl_tevent tcl_tevent.cpp 
"libdm;libbu;${TCL_LIBRARY};${TK_LIBRARY}" TEST_USEDATA)
+endif (BRLCAD_ENABLE_TK)
+
 CMAKEFILES(CMakeLists.txt)
+CMAKEFILES(tcl_img.cpp)
+CMAKEFILES(tcl_tevent.cpp)
 
 # Local Variables:
 # tab-width: 8
@@ -44,4 +51,5 @@
 # mode: cmake
 # indent-tabs-mode: t
 # End:
-# ex: shiftwidth=2 tabstop=8 textwidth=0 wrapmargin=0
+# ex: shiftwidth=2 tabstop=8
+

Added: brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_img.cpp
===================================================================
--- brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_img.cpp                     
        (rev 0)
+++ brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_img.cpp     2020-04-29 
16:02:41 UTC (rev 75630)
@@ -0,0 +1,603 @@
+/*                     T C L _ I M G . C P P
+ * BRL-CAD
+ *
+ * Published in 2020 by the United States Government.
+ * This work is in the public domain.
+ *
+ */
+/** @file tcl_img.cpp
+ *
+ * Self contained example of working with image data
+ * in Tcl/Tk in C/C++
+ *
+ * Eventually this should probably turn into a Togl-esque
+ * widget that we properly include, so we ensure that our
+ * window is behaving the way Tk expects it to for things
+ * like refresh.  Our current behavior in that regard is
+ * a bit iffy - ogl works but I'm not convinced that's
+ * because we're doing the Tcl/Tk bits right, and X is
+ * doing low level blitting and other X calls (which
+ * isn't great for maintainability/portability and
+ * can be a headache for debugging.)
+ *
+ */
+
+#include "common.h"
+
+#include <climits>
+#include <random>
+#include <iostream>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "bu/malloc.h"
+
+#include "tcl.h"
+#include "tk.h"
+
+const char *DM_PHOTO = ".dm0.photo";
+const char *DM_CANVAS = ".dm0";
+
+TCL_DECLARE_MUTEX(dilock)
+TCL_DECLARE_MUTEX(threadMutex)
+
+/* Container holding image generation information - need to be able
+ * to pass these to the update command */
+struct img_data {
+    // These flags should be mutex guarded.
+    int render_needed;
+    int render_running;
+    int render_ready;
+
+    // Parent application sets this when it's time to shut
+    // down the dm rendering
+    int render_shutdown;
+
+    // Main thread id - used to send a rendering event back to
+    // the parent thread.
+    Tcl_ThreadId parent_id;
+
+    // Tcl interp can only be used by the thread that created it, and the
+    // Tk_Photo calls require an interpreter.  Thus we keep track of which
+    // interpreter is the parent interp in the primary data container, as this
+    // interp (and ONLY this interp) is requried to do Tk_Photo processing when
+    // triggered from thread events sent back to the parent.  Those callbacks
+    // don't by default encode knowledge about the parent's Tcl_Interp, so we
+    // store it where we know we can access it.
+    Tcl_Interp *parent_interp;
+
+    // Manager thread id
+    Tcl_ThreadId manager_id;
+
+    // Image info.  Reflects the currently requested parent image
+    // size - the render thread may have a different size during
+    // rendering, as it "snapshots" these numbers to have a stable
+    // buffer size when actually writing pixels.
+    int width;
+    int height;
+
+    // Actual allocated sizes of image buffer
+    int bwidth;
+    int bheight;
+
+    // Used for image generation - these are presisted across renders so the
+    // image contents will vary as expected.
+    std::default_random_engine *gen;
+    std::uniform_int_distribution<int> *colors;
+    std::uniform_int_distribution<int> *vals;
+
+    // The rendering memory used to actually generate the image contents.
+    long buff_size;
+    unsigned char *pixelPtr;
+};
+
+// Event container passed to routines triggered by events.
+struct DmRenderEvent {
+    Tcl_Event event;            /* Must be first */
+    struct img_data *idata;
+};
+
+// Even for events where we don't intend to actually run a proc,
+// we need to tell Tcl it successfully processed them.  For that
+// we define a no-op callback proc.
+static int
+noop_proc(Tcl_Event *UNUSED(evPtr), int UNUSED(mask))
+{
+    // Return one to signify a successful completion of the process execution
+    return 1;
+}
+
+// The actual Tk_Photo updating must be done by the creater of the
+// Tcl_Interp - which is the parent thread.  The interp below must
+// thus be the PARENT's interp, and we have passed it through the
+// various structures to be available here.
+static int
+DmUpdateProc(Tcl_Event *evPtr, int UNUSED(mask))
+{
+    struct DmRenderEvent *DmEventPtr = (DmRenderEvent *) evPtr;
+    struct img_data *idata = DmEventPtr->idata;
+    Tcl_Interp *interp = idata->parent_interp;
+    int width = idata->bwidth;
+    int height = idata->bheight;
+
+    // Let Tcl/Tk know the photo data has changed, so it can update the visuals
+    // accordingly
+    Tk_PhotoImageBlock dm_data;
+    Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
+    Tk_PhotoGetImage(dm_img, &dm_data);
+    if (dm_data.width != width || dm_data.height != height) {
+       Tk_PhotoSetSize(interp, dm_img, width, height);
+       Tk_PhotoGetImage(dm_img, &dm_data);
+    }
+
+    Tcl_MutexLock(&dilock);
+
+    // Tk_PhotoPutBlock appears to be making a copy of the data, so we should
+    // be able to point to our thread's rendered data to feed it in for
+    // copying without worrying that Tk might alter it.
+    dm_data.pixelPtr = idata->pixelPtr;
+    Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width, 
dm_data.height, TK_PHOTO_COMPOSITE_SET);
+
+    // Render processed - reset the ready flag
+    idata->render_ready = 0;
+    Tcl_MutexUnlock(&dilock);
+
+    // Return one to signify a successful completion of the process execution
+    return 1;
+}
+
+int
+image_update_data(ClientData clientData, Tcl_Interp *interp, int argc, char 
**argv)
+{
+    struct img_data *idata = (struct img_data *)clientData;
+
+    if (argc == 3) {
+       // Unpack the current width and height.  If these don't match what 
idata has
+       // or thinks its working on, update the image size.
+       // (Note: checking errno, although it may not truly be necessary if we
+       // trust Tk to always give us valid coordinates...)
+       char *p_end;
+       errno = 0;
+       long width = strtol(argv[1], &p_end, 10);
+       if (errno == ERANGE || (errno != 0 && width == 0) || p_end == argv[1]) {
+           std::cerr << "Invalid width: " << argv[1] << "\n";
+           return TCL_ERROR;
+       }
+       errno = 0;
+       long height = strtol(argv[2], &p_end, 10);
+       if (errno == ERANGE || (errno != 0 && height == 0) || p_end == argv[1]) 
{
+           std::cerr << "Invalid height: " << argv[2] << "\n";
+           return TCL_ERROR;
+       }
+
+       Tcl_MutexLock(&dilock);
+       idata->width = width;
+       idata->height = height;
+       Tcl_MutexUnlock(&dilock);
+
+       Tk_PhotoImageBlock dm_data;
+       Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
+       Tk_PhotoGetImage(dm_img, &dm_data);
+       if (dm_data.width != idata->width || dm_data.height != idata->height) {
+           Tk_PhotoSetSize(interp, dm_img, idata->width, idata->height);
+       }
+    }
+
+    // Generate an event for the manager thread to let it know we need work
+    Tcl_MutexLock(&threadMutex);
+    struct DmRenderEvent *threadEventPtr = (struct DmRenderEvent 
*)ckalloc(sizeof(DmRenderEvent));
+    threadEventPtr->idata = idata;
+    threadEventPtr->event.proc = noop_proc;
+    Tcl_ThreadQueueEvent(idata->manager_id, (Tcl_Event *) threadEventPtr, 
TCL_QUEUE_TAIL);
+    Tcl_ThreadAlert(idata->manager_id);
+    Tcl_MutexUnlock(&threadMutex);
+
+    return TCL_OK;
+}
+
+// Given an X,Y coordinate in a TkPhoto image and a desired offset in
+// X and Y, return the index value into the pixelPtr array N such that
+// N is the integer value of the R color at that X,Y coordiante, N+1
+// is the G value, N+2 is the B value and N+3 is the Alpha value.
+// If either desired offset is beyond the width and height boundaries,
+// cap the return at the minimum/maximum allowed value.
+static int
+img_xy_index(int width, int height, int x, int y, int dx, int dy)
+{
+    int nx = ((x + dx) > width)  ? width  : ((x + dx) < 0) ? 0 : x + dx;
+    int ny = ((y + dy) > height) ? height : ((y + dy) < 0) ? 0 : y + dy;
+    return (ny * width * 4) + nx * 4;
+}
+
+int
+image_paint_xy(ClientData UNUSED(clientData), Tcl_Interp *interp, int argc, 
char **argv)
+{
+    if (argc != 3) {
+       std::cerr << "Unexpected argc: " << argc << "\n";
+       return TCL_ERROR;
+    }
+
+    // Unpack the coordinates (checking errno, although it may not truly be
+    // necessary if we trust Tk to always give us valid coordinates...)
+    char *p_end;
+    errno = 0;
+    long xcoor = strtol(argv[1], &p_end, 10);
+    if (errno == ERANGE || (errno != 0 && xcoor == 0) || p_end == argv[1]) {
+       std::cerr << "Invalid image_paint_xy X coordinate: " << argv[1] << "\n";
+       return TCL_ERROR;
+    }
+    errno = 0;
+    long ycoor = strtol(argv[2], &p_end, 10);
+    if (errno == ERANGE || (errno != 0 && ycoor == 0) || p_end == argv[1]) {
+       std::cerr << "Invalid image_paint_xy Y coordinate: " << argv[2] << "\n";
+       return TCL_ERROR;
+    }
+
+    // Look up the internals of the image - we're going to directly manipulate
+    // the values of the image to simulate a display manager or framebuffer
+    // changing the visual via rendering.
+    Tk_PhotoImageBlock dm_data;
+    Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
+    Tk_PhotoGetImage(dm_img, &dm_data);
+
+
+    for (int i = -2; i < 3; i++) {
+       for (int j = -2; j < 3; j++) {
+           int pindex = img_xy_index(dm_data.width, dm_data.height, xcoor, 
ycoor, i, j);
+           // Set to opaque white
+           dm_data.pixelPtr[pindex] = 255;
+           dm_data.pixelPtr[pindex+1] = 255;
+           dm_data.pixelPtr[pindex+2] = 255;
+           dm_data.pixelPtr[pindex+3] = 255;
+       }
+    }
+
+    // Let Tcl/Tk know the photo data has changed, so it can update the 
visuals accordingly.
+    Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width, 
dm_data.height, TK_PHOTO_COMPOSITE_SET);
+
+    return TCL_OK;
+}
+
+static Tcl_ThreadCreateType
+Dm_Render(ClientData clientData)
+{
+    // Unpack the shared data object
+    struct img_data *idata = (struct img_data *)clientData;
+
+    // Lock updating of the idata values until we've gotten this run's key
+    // information - we don't want this changing mid-render.
+    Tcl_MutexLock(&dilock);
+
+    // Anything before this point will be convered by this render.  (This
+    // flag may get set after this render starts by additional events, but
+    // to this point this rendering will handle it.)
+    idata->render_needed = 0;
+
+    // We're now in process - don't start another frame until this one
+    // is complete.
+    idata->render_running = 1;
+
+    // Until we're done, make sure nobody thinks we are ready
+    idata->render_ready = 0;
+
+    // Lock in this width and height - that's what we've got memory for,
+    // so that's what the remainder of this rendering pass will use.
+    idata->bwidth = idata->width;
+    idata->bheight = idata->height;
+
+    // If we have insufficient memory, allocate or reallocate a local
+    // buffer big enough for the purpose.  We use our own buffer for
+    // rendering to allow Tk full control over what it wants to do behind
+    // the scenes.  We need this memory to persist, so handle it from the
+    // management thread.
+    long b_minsize = idata->bwidth * idata->bheight * 4;
+    if (!idata->pixelPtr || idata->buff_size < b_minsize) {
+       idata->pixelPtr = (unsigned char *)bu_realloc(idata->pixelPtr, 
2*b_minsize*sizeof(char), "realloc pixbuf");
+       idata->buff_size = b_minsize * 2;
+    }
+
+    // Have the key values now, we can unlock and allow interactivity in
+    // the parent.  We'll finish this rendering pass before paying any
+    // further attention to parent settings, but the parent may now set
+    // them for future consideration.
+    Tcl_MutexUnlock(&dilock);
+
+    
//////////////////////////////////////////////////////////////////////////////
+    // Rendering operation - this is where the work of a DM/FB render pass
+    // would occur in a real application
+    
//////////////////////////////////////////////////////////////////////////////
+    // To get a little visual variation and make it easer to see changes,
+    // randomly turn on/off the individual colors for each pass.
+    int r = (*idata->colors)((*idata->gen));
+    int g = (*idata->colors)((*idata->gen));
+    int b = (*idata->colors)((*idata->gen));
+
+    // For each pixel, get a random color value and set it in the image memory 
buffer.
+    // This alters the actual data, but Tcl/Tk doesn't know about it yet.
+    for (int i = 0; i < b_minsize; i+=4) {
+       // Red
+       idata->pixelPtr[i] = (r) ? (*idata->vals)((*idata->gen)) : 0;
+       // Green
+       idata->pixelPtr[i+1] = (g) ? (*idata->vals)((*idata->gen)) : 0;
+       // Blue
+       idata->pixelPtr[i+2] = (b) ? (*idata->vals)((*idata->gen)) : 0;
+       // Alpha stays at 255 (Don't really need to set it since it should
+       // already be set, just doing so for local clarity about what should be
+       // happening with the buffer data...)
+       idata->pixelPtr[i+3] = 255;
+    }
+    
//////////////////////////////////////////////////////////////////////////////
+    
//////////////////////////////////////////////////////////////////////////////
+
+    // Update done - let the parent structure know.  We don't clear the
+    // render_needed flag here, since the parent window may have changed
+    // between the start and the end of this render and if it has we need
+    // to start over.
+    Tcl_MutexLock(&dilock);
+    idata->render_running = 0;
+    idata->render_ready = 1;
+    Tcl_MutexUnlock(&dilock);
+
+    // Generate an event for the manager thread to let it know we're done
+    Tcl_MutexLock(&threadMutex);
+    struct DmRenderEvent *threadEventPtr = (struct DmRenderEvent 
*)ckalloc(sizeof(DmRenderEvent));
+    threadEventPtr->idata = idata;
+    threadEventPtr->event.proc = noop_proc;
+    Tcl_ThreadQueueEvent(idata->manager_id, (Tcl_Event *) threadEventPtr, 
TCL_QUEUE_TAIL);
+    Tcl_ThreadAlert(idata->manager_id);
+    Tcl_MutexUnlock(&threadMutex);
+
+    // Render complete, we're done with this thread
+    Tcl_ExitThread(TCL_OK);
+    TCL_THREAD_CREATE_RETURN;
+}
+
+static Tcl_ThreadCreateType
+Dm_Update_Manager(ClientData clientData)
+{
+    // This thread needs to process events - give it its own interp so it can 
do so.
+    Tcl_Interp *interp = Tcl_CreateInterp();
+    Tcl_Init(interp);
+
+    // Unpack the shared data object
+    struct img_data *idata = (struct img_data *)clientData;
+
+    // Set up our data sources - we'll reuse these for each render, so they are
+    // set up in the managing thread
+    std::default_random_engine gen;
+    std::uniform_int_distribution<int> colors(0,1);
+    std::uniform_int_distribution<int> vals(0,255);
+    idata->gen = &gen;
+    idata->colors = &colors;
+    idata->vals = &vals;
+
+    // Until we're shutting down, check for and process events - this thread 
will persist
+    // for the life of the application.
+    while (!idata->render_shutdown) {
+
+       // Events can come from either the parent (requesting a rendering, 
announcing a shutdown)
+       // or from the rendering thread (announcing completion of a rendering.)
+       Tcl_DoOneEvent(0);
+
+       // If anyone flipped the shutdown switch while we were waiting on an
+       // event, don't do any more work - there's no point.
+       if (idata->render_shutdown) {
+           continue;
+       }
+
+       // If we have a render ready, let the parent know to update the GUI 
before we
+       // go any further.  Even if a completed render is out of data, it's 
closer to
+       // current than what was previously displayed so the application should 
go ahead
+       // and show it.
+       if (idata->render_ready) {
+
+           // Generate an event for the parent thread - its time to update the
+           // Tk_Photo in the GUI that's holding the image
+           Tcl_MutexLock(&threadMutex);
+           struct DmRenderEvent *threadEventPtr = (struct DmRenderEvent 
*)ckalloc(sizeof(DmRenderEvent));
+           threadEventPtr->idata = idata;
+           threadEventPtr->event.proc = DmUpdateProc;
+           Tcl_ThreadQueueEvent(idata->parent_id, (Tcl_Event *) 
threadEventPtr, TCL_QUEUE_TAIL);
+           Tcl_ThreadAlert(idata->parent_id);
+           Tcl_MutexUnlock(&threadMutex);
+
+           // If we don't have any work pending, go back to waiting.  If we 
know
+           // we're already out of date, proceed without waiting for another 
event
+           // from the parent - we've already had one.
+           if (!idata->render_needed) {
+               continue;
+           }
+       }
+
+       // If we're already rendering and we have another rendering event, it's
+       // an update request from the parent - set the flag and continue
+       if (idata->render_running) {
+           idata->render_needed = 1;
+           continue;
+       }
+
+       // If we need a render and aren't already running, it's time to go to 
work.
+
+
+       // Start a rendering thread.
+       Tcl_ThreadId threadID;
+       if (Tcl_CreateThread(&threadID, Dm_Render, (ClientData)idata, 
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_NOFLAGS) != TCL_OK) {
+           std::cerr << "can't create render thread\n";
+       }
+    }
+
+    // We're well and truly done - the application is closing down - free the
+    // rendering buffer and quit the thread
+    if (idata->pixelPtr) {
+       bu_free(idata->pixelPtr, "free pixbuf");
+    }
+    Tcl_ExitThread(TCL_OK);
+    TCL_THREAD_CREATE_RETURN;
+}
+
+int
+main(int UNUSED(argc), const char *argv[])
+{
+    std::string bind_cmd;
+
+    // For now, just use a fixed 512x512 image size
+    int wsize = 512;
+
+    // Set up random image data generation so we can simulate a raytrace or
+    // raster changing the view data.  We need to use these for subsequent
+    // image updating, so pack them into a structure we can pass through Tcl's
+    // evaluations.
+    struct img_data *idata;
+    BU_GET(idata, struct img_data);
+    idata->render_needed = 1;
+    idata->render_running = 0;
+    idata->render_ready = 0;
+    idata->render_shutdown = 0;
+    idata->pixelPtr = NULL;
+    idata->parent_id = Tcl_GetCurrentThread();
+
+    // Set up Tcl/Tk
+    Tcl_FindExecutable(argv[0]);
+    Tcl_Interp *interp = Tcl_CreateInterp();
+    Tcl_Init(interp);
+    Tk_Init(interp);
+
+    // Save a pointer so we can get at the interp later
+    idata->parent_interp = interp;
+
+    // Make a simple toplevel window
+    Tk_Window tkwin = Tk_MainWindow(interp);
+    Tk_GeometryRequest(tkwin, wsize, wsize);
+    Tk_MakeWindowExist(tkwin);
+    Tk_MapWindow(tkwin);
+
+    // Create the (initially empty) Tcl/Tk Photo (a.k.a color image) we will
+    // use as our rendering canvas for the images.
+    //
+    // Note: confirmed with Tcl/Tk community that (at least as of Tcl/Tk 8.6)
+    // Tcl_Eval is the ONLY way to create an image object.  The C API doesn't
+    // expose that ability, although it does support manipulation of the
+    // created object.
+    std::string img_cmd = std::string("image create photo ") + 
std::string(DM_PHOTO);
+    Tcl_Eval(interp, img_cmd.c_str());
+    Tk_PhotoHandle dm_img = Tk_FindPhoto(interp, DM_PHOTO);
+    Tk_PhotoBlank(dm_img);
+    Tk_PhotoSetSize(interp, dm_img, wsize, wsize);
+
+    // Initialize the PhotoImageBlock information for a color image of size
+    // 500x500 pixels.
+    Tk_PhotoImageBlock dm_data;
+    dm_data.width = wsize;
+    dm_data.height = wsize;
+    dm_data.pixelSize = 4;
+    dm_data.pitch = wsize * 4;
+    dm_data.offset[0] = 0;
+    dm_data.offset[1] = 1;
+    dm_data.offset[2] = 2;
+    dm_data.offset[3] = 3;
+
+    // Actually create our memory for the image buffer.  Expects RGBA 
information
+    dm_data.pixelPtr = (unsigned char *)Tcl_AttemptAlloc(dm_data.width * 
dm_data.height * 4);
+    if (!dm_data.pixelPtr) {
+       std::cerr << "Tcl/Tk photo memory allocation failed!\n";
+       exit(1);
+    }
+
+    // Initialize. This alters the actual data, but Tcl/Tk doesn't know about 
it yet.
+    for (int i = 0; i < (dm_data.width * dm_data.height * 4); i+=4) {
+       // Red
+       dm_data.pixelPtr[i] = 0;
+       // Green
+       dm_data.pixelPtr[i+1] = 255;
+       // Blue
+       dm_data.pixelPtr[i+2] = 0;
+       // Alpha at 255 - we dont' want transparency for this demo.
+       dm_data.pixelPtr[i+3] = 255;
+    }
+
+    // Let Tk_Photo know we have data
+    Tk_PhotoPutBlock(interp, dm_img, &dm_data, 0, 0, dm_data.width, 
dm_data.height, TK_PHOTO_COMPOSITE_SET);
+
+    // We now have a window - set the default idata size
+    idata->width = dm_data.width;
+    idata->height = dm_data.height;
+
+    // Define a canvas widget, pack it into the root window, and associate the 
image with it
+    // TODO - should the canvas be inside a frame?
+    std::string canvas_cmd = std::string("canvas ") + std::string(DM_CANVAS) + 
std::string(" -width ") + std::to_string(wsize) + std::string(" -height ")  + 
std::to_string(wsize) + std::string(" -borderwidth 0");
+    Tcl_Eval(interp, canvas_cmd.c_str());
+    std::string pack_cmd = std::string("pack ") + std::string(DM_CANVAS) + 
std::string(" -fill both -expand 1");
+    Tcl_Eval(interp, pack_cmd.c_str());
+    std::string canvas_img_cmd = std::string(DM_CANVAS) + std::string(" create 
image 0 0 -image ") + std::string(DM_PHOTO) + std::string(" -anchor nw");
+    Tcl_Eval(interp, canvas_img_cmd.c_str());
+
+
+    // Register a paint command so we can change the image contents near the 
cursor position
+    (void)Tcl_CreateCommand(interp, "image_paint", (Tcl_CmdProc 
*)image_paint_xy, NULL, (Tcl_CmdDeleteProc* )NULL);
+    // Establish the Button-1+Motion combination event as the trigger for 
drawing on the image
+    bind_cmd = std::string("bind . <B1-Motion> {image_paint %x %y}");
+    Tcl_Eval(interp, bind_cmd.c_str());
+
+
+    // Register an update command so we can change the image contents
+    (void)Tcl_CreateCommand(interp, "image_update", (Tcl_CmdProc 
*)image_update_data, (ClientData)idata, (Tcl_CmdDeleteProc* )NULL);
+    // Establish the Button-2 and Button-3 event as triggers for updating the 
image contents
+    bind_cmd = std::string("bind . <Button-2> {image_update}");
+    Tcl_Eval(interp, bind_cmd.c_str());
+    bind_cmd = std::string("bind . <Button-3> {image_update}");
+    Tcl_Eval(interp, bind_cmd.c_str());
+
+    // Update the photo if the window changes
+    bind_cmd = std::string("bind ") + std::string(DM_CANVAS) + std::string(" 
<Configure> {image_update [winfo width %W] [winfo height %W]\"}");
+    Tcl_Eval(interp, bind_cmd.c_str());
+
+    // Multithreading experiment
+    Tcl_ThreadId threadID;
+    if (Tcl_CreateThread(&threadID, Dm_Update_Manager, (ClientData)idata, 
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
+       std::cerr << "can't create thread\n";
+    }
+    idata->manager_id = threadID;
+
+    // Enter the main applicatio loop - the initial image will appear, and 
Button-1 mouse
+    // clicks on the window should generate and display new images
+    while (1) {
+       Tcl_DoOneEvent(0);
+       if (!Tk_GetNumMainWindows()) {
+
+           // If we've closed the window, we're done - start spreading the word
+           Tcl_MutexLock(&dilock);
+           idata->render_shutdown = 1;
+
+           // After setting the shutdown flag, send an event to wake up the 
update manager thread.
+           // It will see the change in status and proceed to shut itself down.
+           struct DmRenderEvent *threadEventPtr = (struct DmRenderEvent 
*)ckalloc(sizeof(DmRenderEvent));
+           threadEventPtr->idata = idata;
+           threadEventPtr->event.proc = noop_proc;
+           Tcl_ThreadQueueEvent(idata->manager_id, (Tcl_Event *) 
threadEventPtr, TCL_QUEUE_TAIL);
+           Tcl_ThreadAlert(idata->manager_id);
+           Tcl_MutexUnlock(&dilock);
+
+           // Wait for the thread cleanup to complete
+           int tret;
+           Tcl_JoinThread(threadID, &tret);
+           if (tret != TCL_OK) {
+               std::cerr << "Failed to shut down display management thread\n";
+           } else {
+               std::cout << "Successful display management thread shutdown\n";
+           }
+
+           // Clean up memory and exit
+           BU_PUT(idata, struct img_data);
+           exit(0);
+       }
+    }
+}
+
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8


Property changes on: brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_img.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_tevent.cpp
===================================================================
--- brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_tevent.cpp                  
        (rev 0)
+++ brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_tevent.cpp  2020-04-29 
16:02:41 UTC (rev 75630)
@@ -0,0 +1,196 @@
+/*              T C L _ T E V E N T . C P P
+ * BRL-CAD
+ *
+ * Published in 2020 by the United States Government.
+ * This work is in the public domain.
+ *
+ */
+/** @file tcl_tevent.cpp
+ *
+ */
+
+#include "common.h"
+
+#include <iostream>
+
+#include "tcl.h"
+#include "tk.h"
+
+TCL_DECLARE_MUTEX(calclock)
+TCL_DECLARE_MUTEX(threadMutex)
+
+struct cdata {
+    int val1;
+    int val2;
+    int val3;
+    int shutdown;
+    Tcl_ThreadId parent_id;
+    Tcl_ThreadId worker_id;
+    Tcl_Interp *parent_interp;
+};
+
+struct EventResult {
+    Tcl_Condition done;
+    int ret;
+};
+
+struct UpdateEvent {
+    Tcl_Event event;            /* Must be first */
+    struct EventResult *result;
+    struct cdata *d;
+};
+
+struct CalcEvent {
+    Tcl_Event event;            /* Must be first */
+    struct EventResult *result;
+    struct cdata *d;
+};
+
+
+static int
+UpdateProc1(Tcl_Event *evPtr, int UNUSED(mask))
+{
+    struct UpdateEvent *EventPtr = (UpdateEvent *) evPtr;
+    struct cdata *d = EventPtr->d;
+
+    Tcl_MutexLock(&calclock);
+    d->val1 = d->val1 + 1000;
+    Tcl_MutexUnlock(&calclock);
+
+    std::cout << "val1: " << d->val1 << "\n";
+
+    // Return one to signify a successful completion of the process execution
+    return 1;
+}
+
+static int
+UpdateProc3(Tcl_Event *evPtr, int UNUSED(mask))
+{
+    struct UpdateEvent *EventPtr = (UpdateEvent *) evPtr;
+    struct cdata *d = EventPtr->d;
+
+    Tcl_MutexLock(&calclock);
+    d->val3 = d->val3 + 2000;
+    Tcl_MutexUnlock(&calclock);
+
+    std::cout << "val3: " << d->val3 << "\n";
+
+    // Return one to signify a successful completion of the process execution
+    return 1;
+}
+
+static Tcl_ThreadCreateType
+Calc_Thread(ClientData clientData)
+{
+    struct cdata *d = (struct cdata *)clientData;
+    Tcl_Interp *interp = Tcl_CreateInterp();
+    Tcl_Init(interp);
+
+    while (!d->shutdown) {
+
+       Tcl_DoOneEvent(0);
+
+       Tcl_MutexLock(&calclock);
+       d->val2 = d->val2 + 500;
+       Tcl_MutexUnlock(&calclock);
+
+       std::cout << "val2: " << d->val2 << "\n";
+       Tcl_Sleep(100);
+
+       // Generate an event for the parent thread to let it know to update
+       // based on the calculation result
+       struct UpdateEvent *threadEventPtr = (struct UpdateEvent 
*)ckalloc(sizeof(UpdateEvent));
+       threadEventPtr->d = d;
+       threadEventPtr->event.proc = UpdateProc3;
+       Tcl_MutexLock(&threadMutex);
+       Tcl_ThreadQueueEvent(d->parent_id, (Tcl_Event *) threadEventPtr, 
TCL_QUEUE_TAIL);
+       Tcl_ThreadAlert(d->parent_id);
+       Tcl_MutexUnlock(&threadMutex);
+    }
+
+    // We're well and truly done - the application is closing down
+    Tcl_ExitThread(TCL_OK);
+    TCL_THREAD_CREATE_RETURN;
+}
+
+int
+calc_f1(ClientData clientData, Tcl_Interp *UNUSED(interp), int UNUSED(argc), 
char **UNUSED(argv))
+{
+    struct cdata *d = (struct cdata *)clientData;
+    // Generate an event for the worker thread to let it know to update
+    // based on the calculation result
+    struct UpdateEvent *threadEventPtr = (struct UpdateEvent 
*)ckalloc(sizeof(UpdateEvent));
+    threadEventPtr->d = d;
+    threadEventPtr->event.proc = UpdateProc1;
+    Tcl_MutexLock(&threadMutex);
+    Tcl_ThreadQueueEvent(d->worker_id, (Tcl_Event *) threadEventPtr, 
TCL_QUEUE_TAIL);
+    Tcl_ThreadAlert(d->worker_id);
+    Tcl_MutexUnlock(&threadMutex);
+
+    return TCL_OK;
+}
+
+int
+main(int UNUSED(argc), const char *argv[])
+{
+    struct cdata ddata;
+    ddata.val1 = 0;
+    ddata.val2 = 0;
+    ddata.val3 = 0;
+    ddata.shutdown = 0;
+    ddata.parent_id = Tcl_GetCurrentThread();
+
+    // Set up Tcl/Tk
+    Tcl_FindExecutable(argv[0]);
+    Tcl_Interp *interp = Tcl_CreateInterp();
+    Tcl_Init(interp);
+    Tk_Init(interp);
+
+    // Save a pointer so we can get at the interp later
+    ddata.parent_interp = interp;
+
+    // Make a simple toplevel window
+    Tk_Window tkwin = Tk_MainWindow(interp);
+    Tk_GeometryRequest(tkwin, 512, 512);
+    Tk_MakeWindowExist(tkwin);
+    Tk_MapWindow(tkwin);
+
+    // Whenever the window changes, call the incrementer
+    (void)Tcl_CreateCommand(interp, "calc1", (Tcl_CmdProc *)calc_f1, 
(ClientData)&ddata, (Tcl_CmdDeleteProc* )NULL);
+    Tcl_Eval(interp, "bind . <Configure> {calc1}");
+
+    // Multithreading experiment
+    Tcl_ThreadId threadID;
+    if (Tcl_CreateThread(&threadID, Calc_Thread, (ClientData)&ddata, 
TCL_THREAD_STACK_DEFAULT, TCL_THREAD_JOINABLE) != TCL_OK) {
+       std::cerr << "can't create thread\n";
+    }
+    ddata.worker_id = threadID;
+
+    // Enter the main application loop - the initial image will appear, and 
Button-1 mouse
+    // clicks on the window should generate and display new images
+    while (1) {
+       Tcl_DoOneEvent(0);
+       if (ddata.val3 > 100000 && ddata.val2 > 100000) {
+           Tcl_MutexLock(&calclock);
+           ddata.shutdown = 1;
+           Tcl_MutexUnlock(&calclock);
+           int tret;
+           Tcl_JoinThread(threadID, &tret);
+           if (tret != TCL_OK) {
+               std::cerr << "Failure to shut down work thread\n";
+           } else {
+               std::cout << "Successful work thread shutdown\n";
+           }
+           exit(0);
+       }
+    }
+}
+
+// Local Variables:
+// tab-width: 8
+// mode: C++
+// c-basic-offset: 4
+// indent-tabs-mode: t
+// c-file-style: "stroustrup"
+// End:
+// ex: shiftwidth=4 tabstop=8


Property changes on: brlcad/branches/dm-fb-merge/src/libdm/tests/tcl_tevent.cpp
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: brlcad/branches/dm-fb-merge/src/libged/typein.c
===================================================================
--- brlcad/branches/dm-fb-merge/src/libged/typein.c     2020-04-29 15:00:45 UTC 
(rev 75629)
+++ brlcad/branches/dm-fb-merge/src/libged/typein.c     2020-04-29 16:02:41 UTC 
(rev 75630)
@@ -3110,14 +3110,7 @@
 script_in(struct ged *UNUSED(gedp), const char **cmd_argvs, struct 
rt_db_internal *intern)
 {
     struct rt_script_internal *script_ip;
-    int i=0;
 
-    /* !!! temporary debugging, print out our args */
-    while (cmd_argvs && cmd_argvs[i] != NULL) {
-       bu_log("cmd_argvs[%d] = [%s]\n", i, cmd_argvs[i]);
-       i++;
-    }
-
     intern->idb_type = ID_SCRIPT;
     intern->idb_meth = &OBJ[ID_SCRIPT];
     BU_ALLOC(intern->idb_ptr, struct rt_script_internal);
@@ -3127,9 +3120,6 @@
     bu_vls_init(&script_ip->s_type);
     bu_vls_strcpy(&script_ip->s_type, cmd_argvs[3]);
 
-    /* !!! */
-    bu_log("done creating script object, next it gets exported\n");
-
     return GED_OK;
 }
 

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.



_______________________________________________
BRL-CAD Source Commits mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/brlcad-commits

Reply via email to