Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Users feedback, discussion #4

Open
AlexanderZvyagin opened this issue Feb 22, 2017 · 54 comments
Open

Users feedback, discussion #4

AlexanderZvyagin opened this issue Feb 22, 2017 · 54 comments
Labels

Comments

@AlexanderZvyagin
Copy link

Markus Gans, I am really impressed of the work you have done! In comparision with dialog/newt-snack/ncurses/npyscreen it is like a breath of fresh air. Congratulations and please continue.

I think your library will get more attention if there is a place where users (like me) can ask questions, ask for advices, etc. What shall it be? A mail list? A web forum? Probably the easiest solution (it does not require anything to setup) is to use Stackoverflow with [final-cut] tag?!

@gansm
Copy link
Owner

gansm commented Feb 22, 2017

Many thanks for your reply and your praise.

The using of an alternative and more established place for the user feedback sounds good. I joined Stack Overflow now. You can find me here: http://stackoverflow.com/users/story/7607413

Unfortunately, I can not create the tag "final-cut" without asking a question.

@gansm gansm added the question label Feb 22, 2017
@AlexanderZvyagin
Copy link
Author

http://stackoverflow.com/questions/42408243/support-for-table-grid-widget-in-final-cut
About tag "final-cut": I need to increase my reputation on stackoverflow to be able to create tags.

@helmishariff
Copy link

Can it work on MacOS?

@gansm
Copy link
Owner

gansm commented Dec 2, 2017

I don't have a macOS on my computer, but I'm using a compile-run with macOS on Travis-CI. The use of macOS should therefore be possible. Of course, I would appreciate a positive feedback.

https://travis-ci.org/gansm/finalcut/builds/304405079
https://travis-ci.org/gansm/finalcut/jobs/304405082

@helmishariff
Copy link

helmishariff commented Dec 2, 2017

I got this error message when try to compile hello sample.
fterm.h:57:12: fatal error: 'gpm.h' file not found
#include <gpm.h> (if this is mouse daemon, I can't find for MacOS ver.)

@helmishariff
Copy link

I have checked emac in Macport. This is how they implement the mouse. https://searchcode.com/codesearch/view/27232327/

@helmishariff
Copy link

helmishariff commented Dec 2, 2017

I have download gpm from github since it can support xterm. Then I compile with g++ -I/Library/finalcut/include -o hellofinal hellofinal.cpp and I get linker error:
Undefined symbols for architecture x86_64:
"FMessageBox::setText(FString const&)", referenced from:
_main in hellofinal-0f9b96.o
"FMessageBox::FMessageBox(FWidget*)", referenced from:
_main in hellofinal-0f9b96.o
"FMessageBox::~FMessageBox()", referenced from:
_main in hellofinal-0f9b96.o
"FApplication::FApplication(int const&, char**, bool)", referenced from:
_main in hellofinal-0f9b96.o
"FApplication::~FApplication()", referenced from:
_main in hellofinal-0f9b96.o
"FDialog::exec()", referenced from:
_main in hellofinal-0f9b96.o
"FString::FString(char const*)", referenced from:
_main in hellofinal-0f9b96.o
"FString::~FString()", referenced from:
_main in hellofinal-0f9b96.o

Then I tried to compile with g++ -I/Library/finalcut/include -o wtui hellofinal.cpp /Library/finalcut/src/fmessagebox.cpp get this error:
error: "Only <final/final.h> can be included directly."
#error "Only <final/final.h> can be included directly."

@helmishariff
Copy link

It seems the examples all working when I use make.

@helmishariff
Copy link

When I try to compile manually, error: "_tgetent", referenced from:
FTerm::init_termcaps() in fterm.o
"_tgetflag", referenced from:
FTerm::init_termcaps() in fterm.o
FTerm::init_termcaps_booleans() in fterm.o

@AlexanderZvyagin
Copy link
Author

I confirm that the library works on MacOS.

@helmishariff
Copy link

Yes I know it is working on MacOS since I can make samples & run it. May I know what is c++ flag to compile it.I tried g++ -I/mylibrary/finalcut/include /mylibrary/finalcut/src/*.o -o hello hello.cpp but no success

@gansm
Copy link
Owner

gansm commented Dec 3, 2017

Thank you for your feedback. I'm a little bit confused that the configure script has found a gpm.h on your system.

You should try the following commands:

autoreconf -v --install --force
./configure --prefix=/usr
make

If that doesn't help, you can also explicitly disable GPM support:

./configure --prefix=/usr --without-gpm
make

How to use the library (a little example):

#include <final/final.h>

using namespace finalcut;

int main (int argc, char* argv[])
{
  FApplication app(argc, argv);
  // Note: Use Shift+F10 or Ctrl+^ to close the dialog
  FDialog dialog(&app);
  dialog.setText ("A dialog");
  dialog.setGeometry (FPoint(25, 5), FSize(30, 10));
  app.setMainWidget(&dialog);
  dialog.show();
  return app.exec();
}

Compile with:
g++ -O2 -lfinal dialog.cpp -o dialog

It is important that you don't forget to include the libfinal via "-lfinal".

@helmishariff
Copy link

Yes, thank you. With "-lfinal" flag I'm able to compile my code. Thanks.

@helmishariff
Copy link

Are there any possible this can be ported to Windows?

@helmishariff
Copy link

I have downloaded and make & install on ubuntu 17.04 but can't compile my code with same way I compile on Mac. On Mac, I download gpm first before make & install final cut.

@gansm
Copy link
Owner

gansm commented Dec 4, 2017

A port to Microsoft Windows isn’t possible, because there are neither POSIX libraries nor a terminal emulator available.

For the compiling under Ubuntu you probably need some development packages.

apt-get update && apt-get install autotools-dev automake autoconf autoconf-archive libtool libglib2.0-dev libncurses5-dev libgpm-dev

@user1095108
Copy link

I added the [finalcut-tui] tag on SO. Your tui doesn't work too well on my arch linux though within lxterminal :) All blinking and stuff.

@gansm
Copy link
Owner

gansm commented Oct 9, 2018

Thanks for your tagging.

LXTerminal is a VTE-based terminal emulator and should not cause any problems.
Can you please compile Final Cut with ./build.sh debug and then execute the following command:

examples/termcap | tee /tmp/output ; sed -i -n '/^Terminal/,/`--/p' /tmp/output

Post me the output of cat /tmp/output

Do you also get the libtinfo.so displayed when using the ldd command?

ldd src/.libs/libfinal.so | grep tinfo
	libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007fc734d07000)

What does the environment variable $TERM contain?

If all runs correctly, the file /usr/share/terminfo/g/gnome-256color should be used.
Does this file exist on your system?

@user1095108
Copy link

user1095108 commented Oct 9, 2018

--------
FTermcap
--------

Terminal: cygwin

.------------------- debug -------------------
|               Framebuffer bpp: -1
| after init_256colorTerminal(): gnome-256color
|    after parseAnswerbackMsg(): gnome-256color
|            after parseSecDA(): cygwin
|              The SecDA String: \E[>65;5401;1c 
`------------------- debug -------------------

[Booleans]
FTermcap::background_color_erase: true
FTermcap::automatic_left_margin: false
FTermcap::automatic_right_margin: true
FTermcap::eat_nl_glitch: false
FTermcap::ansi_default_color: false
FTermcap::osc_support: false
FTermcap::no_utf8_acs_chars: true

[Numeric]
FTermcap::max_color: 16
FTermcap::tabstop: 8
FTermcap::attr_without_color: 26

[String]
t_bell: ^G 
t_erase_chars: 
t_clear_screen: \E[H\E[J 
t_clr_eos: \E[J 
t_clr_eol: \E[K 
t_clr_bol: \E[1K 
t_cursor_home: \E[H 
t_cursor_to_ll: 
t_carriage_return: ^M 
t_tab: ^I 
t_back_tab: 
t_insert_padding: 
t_insert_character: \E[@ 
t_parm_ich: \E[%p1%d@ 
t_repeat_char: 
t_initialize_color: \E]4;%p1%d;rgb:%p2%{255}%*%{1000}%/%2.2X/%p3%{255}%*%{1000}%/%2.2X/%p4%{255}%*%{1000}%/%2.2X\E\ 
t_initialize_pair: 
t_set_a_foreground: \E[3%p1%{8}%m%d%?%p1%{7}%>%t;1%e;22%;m 
t_set_a_background: \E[4%p1%{8}%m%d%?%p1%{7}%>%t;5%e;25%;m 
t_set_foreground: 
t_set_background: 
t_set_color_pair: 
t_orig_pair: \E[39;49;25m 
t_orig_colors: 
t_no_color_video: 
t_cursor_address: \E[%i%p1%d;%p2%dH 
t_column_address: \E[%i%p1%dG 
t_row_address: \E[%i%p1%dd 
t_cursor_visible: \E[?25h 
t_cursor_invisible: \E[?25l 
t_cursor_normal: \E[?12l\E[?25h 
t_cursor_up: \E[A 
t_cursor_down: \E[B 
t_cursor_left: ^H 
t_cursor_right: \E[C 
t_parm_up_cursor: \E[%p1%dA 
t_parm_down_cursor: \E[%p1%dB 
t_parm_left_cursor: \E[%p1%dD 
t_parm_right_cursor: \E[%p1%dC 
t_save_cursor: \E7 
t_restore_cursor: \E8 
t_scroll_forward: ^J 
t_scroll_reverse: \EM 
t_enter_ca_mode: \E7\E[?47h 
t_exit_ca_mode: \E[2J\E[?47l\E8 
t_enable_acs: \E(B\E)0 
t_enter_bold_mode: \E[1m 
t_exit_bold_mode: \E[22m 
t_enter_dim_mode: 
t_exit_dim_mode: 
t_enter_italics_mode: 
t_exit_italics_mode: 
t_enter_underline_mode: 
t_exit_underline_mode: 
t_enter_blink_mode: \E[5m 
t_exit_blink_mode: \E[25m 
t_enter_reverse_mode: \E[7m 
t_exit_reverse_mode: \E[27m 
t_enter_standout_mode: \E[7m 
t_exit_standout_mode: \E[27m 
t_enter_secure_mode: \E[8m 
t_exit_secure_mode: 
t_enter_protected_mode: 
t_exit_protected_mode: 
t_enter_crossed_out_mode: 
t_exit_crossed_out_mode: 
t_enter_dbl_underline_mode: 
t_exit_dbl_underline_mode: 
t_set_attributes: \E[0%?%p6%t;1%;%?%p1%p3%|%t;7%;%?%p4%t;5%;m%?%p9%t^N%e^O%; 
t_exit_attribute_mode: \E[0m^O 
t_enter_alt_charset_mode: ^N 
t_exit_alt_charset_mode: ^O 
t_enter_pc_charset_mode: \E[11m 
t_exit_pc_charset_mode: \E[10m 
t_enter_insert_mode: \E[4h 
t_exit_insert_mode: \E[4l 
t_enter_am_mode: 
t_exit_am_mode: 
t_acs_chars: +^P,^Q-^X.^Y0\333`^Da\261f\370g\361h\260j\331k\277l\332m\300n\305o~p\304q\304r\304s_t\303u\264v\301w\302x\263y\363z\362{\343|\330}\234~\376 
t_keypad_xmit: 
t_keypad_local: 
t_key_mouse:

xterm-256color

-rw-r--r-- 1 root root 3619 Jan 29 2018 /usr/share/terminfo/g/gnome-256color

Screenshot of "ui" (add blinking in your imagination):
Screenshot

@gansm
Copy link
Owner

gansm commented Oct 9, 2018

That's interesting. I found out that the VTE project has changed the terminal ID from 1 to 65 since version 0.53.0. I have changed the terminal detection in the current commit.

If it doesn't work, please don't put the whole screen output here again. I only need the part from /tmp/output.

@user1095108
Copy link

user1095108 commented Oct 9, 2018 via email

@user1095108
Copy link

Does the library support Windows, DOS, how about the android console?

@gansm
Copy link
Owner

gansm commented Oct 12, 2018

You need an operating system enviroment that implements a POSIX system call API. Under Windows this can be realized with the Cygwin project.

@user1095108
Copy link

Rather than reimplementing Qt, you should strive to provide features, that Qt does not provide. Perhaps web deployment (emscripten has a curses port available) and declarative language support, that compiles into C++. Qt Quick and the associated bloat are a major issue among embedded Qt devs.

@gansm
Copy link
Owner

gansm commented Oct 14, 2018

I'm not trying to reimplement Qt Framework. The basic class design is influenced from Qt and not a one-to-one Qt clone.
A curses library is not required by Final Cut, it has its own cursor optimization and window management. Only a Termcap library with a capability database is required.

termcap: terminal capability database
https://www.systutorials.com/docs/linux/man/5-termcap/

@cybin
Copy link

cybin commented Nov 14, 2019

Hi!

I'm trying to catch the terminal resize event, but don't know how. My first thought was to subclass FWidget and add a onResize(FResizeEvent *event), but it did not work. In the First Steps you explain that most events will be provided by event() of FObject, but all I see is just a call to onTimer() and onUserEvent(). Is there a simple example available about this?

Thanks in advance.

@gansm
Copy link
Owner

gansm commented Nov 14, 2019

FObject is the base class of FWidget that implements the onResize() event for terminal resizing. The resize event is a specific case that only sends events to the application object.

Code example:

#include <final/final.h>

using namespace finalcut;

class FApp : public FApplication
{
  public:
    FApp (const int& _argc, char* _argv[])
      : FApplication(_argc, _argv)
    { }

  private:
    void onResize (FResizeEvent* ev) override
    {
      FTerm::beep();
      FWidget::onResize (ev);
    }
};

int main (int argc, char* argv[])
{
  FApp app(argc, argv);
  FMessageBox mbox(&app);
  mbox.setTitlebarText("Listen...");
  mbox.setText("Beep on resize.");
  mbox.exec();
}

Maybe the method adjustSize() is more what you are looking for:
https://github.com/gansm/finalcut/blob/master/doc/first-steps.md#dynamic-layout

@cybin
Copy link

cybin commented Nov 15, 2019

Yes, that's what I was up to. Thanks a lot.

@cybin
Copy link

cybin commented Nov 22, 2019

Hi! I've got another question. Since yesterday I'm struggling with a std::thread. Within a subclass of FDialog I want to fill a FListBox with items read by a thread. The thread is created after instantiation. Well, I started with a simple function that just prints that it's been called. But even this thread causes a segmentation fault on thread.join().

void test() { fprintf(stderr, "Test running\n"); }
// void Module::test() { fprintf(stderr, "Test running\n"); }

void Module::revert() {

  std::thread thread_read_communities(test);

  if (thread_read_communities.joinable())
    thread_read_communities.join();
}

Well, Module::test() fails too. To test it, I tried the following simple code snippet:

class test {

public:
        void testfunc() { printf("Thread Running\n"); }

        void main() {

                std::thread foo(&test::testfunc, this);
                foo.join();
        }
};

int main(int arc, char **argv) {

        test bar{};
        bar.main();
        exit(0);
}

This snippet works like charm. I'm not used to C++ so I think I'm missing something important here. Does anyone have got an idea?

Thanks in advance.

@gansm
Copy link
Owner

gansm commented Nov 22, 2019

Unfortunately I don't know your program code, so I can't tell you what could be the reason for the segmentation fault.

This little example might help you:

#include <chrono>
#include <thread>

#include <final/final.h>

using finalcut::FPoint;
using finalcut::FSize;

class ListBoxDialog : public finalcut::FDialog
{
  public:
    ListBoxDialog (FWidget* parent = nullptr) : FDialog(parent)
    {
      setText (L"List copy per thread");
      setGeometry ( FPoint(int(1 + (parent->getWidth() - 41) / 2), 5)
                  , FSize(41, 16) );
      setShadow();
      list1.setGeometry(FPoint(2, 1), FSize(18, 10));
      list1.setText ("Source");
      list1.insert ( {1, 2, 3, 4, 5, 6, 7, 8} );

      list2.setGeometry(FPoint(21, 1), FSize(18, 10));
      list2.setText ("Destination");

      Quit.setGeometry(FPoint(28, 12), FSize(10, 1));
      Quit.setText (L"&Quit");
      Quit.addCallback
      (
        "clicked",
        finalcut::getFApplication(),
        &finalcut::FApplication::cb_exitApp,
        this
      );
    }

    ~ListBoxDialog()
    { }

  private:
    void copy()  // run as thread
    {
      std::this_thread::sleep_for(std::chrono::seconds(2));

      for (std::size_t i = 1; i <= list1.getCount(); i++)
        list2.insert(list1.getItem(i));

      list2.redraw();
      updateTerminal();
    }

    void onShow (finalcut::FShowEvent*)
    {
      std::thread t1(&ListBoxDialog::copy, this);
      t1.detach();
    }

    finalcut::FListBox list1{this};
    finalcut::FListBox list2{this};
    finalcut::FButton  Quit{this};
};

int main (int argc, char* argv[])
{
  finalcut::FApplication app(argc, argv);
  ListBoxDialog dlg(&app);
  app.setMainWidget(&dlg);
  dlg.show();
  return app.exec();
}

Last modified: Code changes for the upcoming release 0.7.0

If you want to learn more about C++, you should take a closer look at these pages.

@cybin
Copy link

cybin commented Nov 25, 2019

Hi Markus!

Thanks for your reply. I figured out that my problem only happens when I statically link the binary. According to this: https://stackoverflow.com/questions/35116327/when-g-static-link-pthread-cause-segmentation-fault-why I needed to -Wl,--whole-archive. Now it just works. Your minimal examples was quite helpful for me. Thanks a lot.

@cybin
Copy link

cybin commented Jan 9, 2020

Thanks for the ComboBox. Today I've added this to my working tree as I didn't find anything similar. That's one thing I've missed for the ComboBox.

void FComboBox::setCurrentItem(std::size_t i) {
  if (i > list_window.list.getCount())
    return;

  list_window.list.setCurrentItem(i);
  input_field = list_window.list.getItem(i).getText();
  input_field.redraw();
  processChanged();
}

If anyone need that too, just add this to the implementation, and declare this method in the header file and you're done. If this is already there, then please, can someone point me to it? Kind regards.

@gansm
Copy link
Owner

gansm commented Jan 9, 2020

You are absolutely right, this important method is indeed missing. Thanks for your sample code, which I have already added to the FComboBox class. (c0b1dc5)

@lighth7015
Copy link

lighth7015 commented Apr 27, 2020

Slack, Discord or Riot are possibilities.

@gansm
Copy link
Owner

gansm commented Apr 27, 2020

I think you are asking me here for the communication channels you can use to ask me about FINAL CUT?

Well, I prefer to use GitHub here to have all the problems and solutions for FINAL CUT on one place. You can also use the "finalcut-tui" tag on Stack Overflow to ask me a question about FINAL CUT.

@davygrvy
Copy link

Can I create windows from a thread different from the main thread that runs the event loop? Or call methods of the widgets? For my app, that I'm about to start writing with finalcut, I will be running a scripting language in a child thread (mainly because it has its own event loop). It isn't a good idea to mix/merge them, though it is possible, but isn't a direction I'd want to go. For sending jobs to the scripting engine, I queue an event to its loop. Should I do the same going back into the finalcut API?

@davygrvy
Copy link

davygrvy commented May 11, 2020

Would it be possible to add a threadsafe wrapper around FApplication::queueEvent() ? https://www.xtof.info/blog/?p=593

@gansm
Copy link
Owner

gansm commented May 11, 2020

I have already posted a small thread example in this issue above. I hope this helps you.

The FINAL CUT main event loop is for processing widget events. I think your implementation of the separation of user interface and script engine is a good idea.

It would be possible to run FApplication::queueEvent() in a separate thread, but that would contradict the actual purpose of this method. FApplication::queueEvent() is intended to process events at a later time that are not time-critical.

@davygrvy
Copy link

davygrvy commented May 11, 2020

I need a mutex protected way to post user events to the main queue which can be called from any foreign thread and wake it if blocked on getch() or wherever the wait is happening. Similar to the use of Tool Command Language's:
Tcl_ThreadQueueEvent(threadID, event, TCL_QUEUE_TAIL); Tcl_ThreadAlert(threadID);

@gansm
Copy link
Owner

gansm commented May 12, 2020

If I understand them correctly, you would like to have the methods

void threadQueueEvent (threadID, FObject*, FEvent*);
and
void threadAlert (threadID);

for threads in addition to

void queueEvent (FObject*, FEvent*);
for single processes.

Sounds interesting! Unfortunately I don't have enough time to implement this at the moment. I'll add it to my "nice to have" list.

@davygrvy
Copy link

Let me explain a bit more from a point-of-view I already have. In windows GUI programming, the central point of event notification for the entire application can be https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex. Adding alertable objects to the list is easy, and a switch/case gets you to the correct handler from the return value. I don't fully get the concept in FINAL CUT where the waitstate is taking place in FApplication::processNextEvent, in ncurses it is getch() I believe, but I would like my handler to be executed from there. I'm trying my best to get up to speed as I really want to use FINAL CUT

@davygrvy
Copy link

Conceptually, I think what I want is to make my own derived class of FApplication that has my own handler for a custom event with a threadsafe ::threadQueueEvent() to be called by a foreign thread. Calling this function inserts my event to the queue and wakes the main thread to service the handlers. This wouldn't be for events to widget objects. Just a way to get into the main thread so I can do stuff like create or close windows and just generally manipulate the UI.

@gansm
Copy link
Owner

gansm commented May 12, 2020

FINAL CUT has no central point of event notification. The general way is to send it directly with FApplication::sendEvent().

FINAL CUT Event Processing

FApplication::processNextEvent() interprets, for example, keyboard and mouse inputs and sends them as events to the currently focused widget. Periodic timer events are also created here.

But if you only want to manipulate GUI elements, you should probably use Signals and Callbacks.

FINAL CUT Signals and Callbacks

@davygrvy
Copy link

davygrvy commented May 12, 2020

There's a select() somewhere. You aren't busy looping on nothing. Callbacks are the wrong direction. I need a way to pull thread context from a foreign thread. Something like this by overriding FApplication::processNextEvent. But what do I do with the conditional to wake up the select/getch where the library idles?

#include <thread>
#include <mutex>
#include <condition_variable>

class Tcl2FCEvent {
public:
    virtual void work() = 0;
};

class MyApp : public FApplication
{
public:
    MyApp(const int& argc, char* argv[], bool = false)
    : FApplication(argc, argv) {}
    
    void threadQueueEvent (Tcl2FCEvent *event) {
        _mutex.lock();
        _Q.emplace_back(event);
        _mutex.unlock();
        _ready.notify_one();
    }

private:
    bool processNextEvent() override {
        if (!_Q.empty()) {
            _mutex.lock();
            Tcl2FCEvent *event = _Q.front();
            _Q.pop_front();
            _mutex.unlock();
            event->work();
            delete event;
        }
        return FApplication::processNextEvent();
    }

    std::deque<Tcl2FCEvent *> _Q;
    std::mutex _mutex;
    std::condition_variable _ready;
};

@gansm
Copy link
Owner

gansm commented May 13, 2020

You are right, the method FKeyboard::isKeyPressed() contains a select() with a timeout of 100 ms. But you can use FVTerm::setNonBlockingRead() to set the timeout to 0.

finalcut::FApplication app{argc, argv};
app.setNonBlockingRead();

I use this e.g. in examples/rotozoomer.cpp to realize fast animation in the terminal.

@davygrvy
Copy link

Oh, that totally changes things. Thanks, I'm understanding your work better. This small change of mine is working so far, but I think I can polish it more as a better interface as I work with FINAL CUT for folks that want to drive the application in alternate ways.

--- a/src/include/final/fapplication.h
+++ b/src/include/final/fapplication.h
@@ -132,6 +132,9 @@ class FApplication : public FWidget
     // Callback method
     void cb_exitApp (const FWidget*, const FDataPtr);
 
+  protected:
+    virtual bool          processNextEvent();
+  
   private:
     // Typedefs
     typedef std::pair<FObject*, std::shared_ptr<FEvent> > eventPair;
@@ -177,7 +180,6 @@ class FApplication : public FWidget
     void                  processMouseEvent();
     void                  processResizeEvent();
     void                  processCloseWidget();
-    bool                  processNextEvent();
     void                  performTimerAction (FObject*, FEvent*) override;
     static bool           isEventProcessable (const FObject*, const FEvent*);

@gansm
Copy link
Owner

gansm commented May 17, 2020

I have been thinking about your solution to provide external program code to the FINAL CUT queue. I have come to the conclusion that an event is not really a good transport vehicle for code to be executed. An event should be a reaction to a status change of the resources to be monitored. Each state transition triggers an event that calls an event handler in the (focused) widget. The widget can then react to the event.

In your case I think the FUserEvent would be the best choice. The FUserEvent can be given a data pointer (which can also be a function pointer). You could use this pointer to send the call to the event handler of the FApplication object. The pointer can then be called in the event handler method onUserEvent().

Here is an example:

#include <chrono>
#include <thread>

#include <final/final.h>

using namespace finalcut;

// Interface class
class Tcl2FCEvent
{
  public:
    virtual void work() = 0;
};

// Implementation class
class myTcl2FCEvent final : public Tcl2FCEvent
{
  public:
    void work() override
    {
      // Beeps in two seconds
      std::this_thread::sleep_for(std::chrono::seconds(2));      
      FTerm::beep();
    }
};

class MyApp : public FApplication
{
  public:
    MyApp(const int& argc, char* argv[])
      : FApplication(argc, argv)
    { }

  private:
    void onUserEvent (FUserEvent* ev) override
    {
      auto obj = ev->getData<Tcl2FCEvent*>();
      obj->work();
    }
};

int main (int argc, char* argv[])
{
  MyApp app(argc, argv);
  FDialog dialog("Dialog", &app);
  dialog.setGeometry (FPoint{25, 5}, FSize{30, 10});
  FWidget::setMainWidget(&dialog);
  dialog.show();

  // Create a user event
  int id = 10;
  FUserEvent user_event(Event::User, id);
  myTcl2FCEvent event_code;
  user_event.setData (&event_code);
  FApplication::sendEvent (&app, &user_event);

  return app.exec();
}

Or you could use thread based notification:

#include <chrono>
#include <condition_variable>
#include <future>
#include <mutex>
#include <thread>

#include <final/final.h>

using namespace finalcut;

std::condition_variable cond_var;
std::mutex mutex;
bool data_ready{false};

// Interface class
class Tcl2FCEvent
{
  public:
    virtual void work() = 0;
};

// Implementation class
class myTcl2FCEvent final : public Tcl2FCEvent
{
  public:
    void work() override
    {
      std::unique_lock<std::mutex> lock(mutex);
      cond_var.wait (lock, []{ return data_ready; });

      // Beeps in two seconds
      std::this_thread::sleep_for(std::chrono::seconds(2));      
      FTerm::beep();
    }
};

class MyApp : public FApplication
{
  public:
    MyApp(const int& argc, char* argv[])
      : FApplication(argc, argv)
    { }

  private:
    void onUserEvent (FUserEvent*) override
    {
      {
        std::lock_guard<std::mutex> lock(mutex);
        data_ready = true;
      }

      cond_var.notify_one();
    }
};

int fc_app (int argc, char* argv[])
{
  MyApp app(argc, argv);
  FDialog dialog("Dialog", &app);
  dialog.setGeometry (FPoint{25, 5}, FSize{30, 10});
  FWidget::setMainWidget(&dialog);
  dialog.show();
  
  // Create a user event
  int id = 10;
  FUserEvent user_event(Event::User, id);
  FApplication::sendEvent (&app, &user_event);

  // Start the application
  return app.exec();
}

int main (int argc, char* argv[])
{
  myTcl2FCEvent event_code;
  std::thread t1(&Tcl2FCEvent::work, &event_code);

  auto app = [&] (std::promise<int>&& p, int argc, char* argv[])
  {
    p.set_value(fc_app(argc,argv));
  };

  std::promise<int> p;
  auto f = p.get_future();
  std::thread t2(app, std::move(p), argc, argv);
  
  t1.join();
  t2.join();
  return f.get();
}

Last modified: Code changes for the new scoped enumeration data types

@davygrvy
Copy link

davygrvy commented May 18, 2020

Thank you for the examples. That's inverted from my usage, though. I would use events to come into FC, not leave it. In other words, while the UI might be idle from mouse moves or keyboard events, I want the scripting language to come into FC to call routines that it can't directly from the scripting thread. work() is used for calling FC routines in the FC thread (main thread), not where the threading takes place. From work(), I'll want to do something with the UI, like create a dialog

@gansm
Copy link
Owner

gansm commented May 21, 2020

I really must thank you for drawing my attention to the event queue. There's been a pretty nasty bug in it. I made a FEvent from every event via std::make_shared() and removed all attributes of the inherited classes. I have now fixed this in the last commit (f20e036).

As for your problem, I think I now know what kind of implementation you are looking for:

#include <chrono>
#include <condition_variable>
#include <future>
#include <mutex>
#include <thread>

#include <final/final.h>

using namespace finalcut;

std::condition_variable cond_var;
std::mutex mutex;
int user_event_id{-1};

// Interface class
class Tcl2FCEvent
{
  public:
    virtual void work(std::future<void>) = 0;
};

// Implementation class
class myTcl2FCEvent final : public Tcl2FCEvent
{
  public:
    void work (std::future<void> f) override
    {
      int color_value{0};
      std::this_thread::sleep_for(std::chrono::milliseconds(250));

      while ( f.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout )
      {
        std::this_thread::sleep_for(std::chrono::milliseconds(250));

        {
          std::lock_guard<std::mutex> lock(mutex);
          user_event_id = color_value;
        }

        cond_var.notify_one();
        color_value++;
        color_value %= 16;
      }
    }
};

class MyApp : public FApplication
{
  public:
    MyApp(const int& argc, char* argv[])
      : FApplication(argc, argv)
    {
      t2 = std::thread(&MyApp::wait, this, std::move(t2_future));
    }

    ~MyApp()
    {
      t2_exit.set_value();  // Stop thread t2
      t2.join();            // Wait until the thread has finished
    }

    void wait (std::future<void> f)
    {
      int id{};

      while ( f.wait_for(std::chrono::milliseconds(1)) == std::future_status::timeout )
      {
        {
          std::unique_lock<std::mutex> lock(mutex);
          cond_var.wait (lock, []{ return ( user_event_id >= 0 ); });
          id = user_event_id;      
          user_event_id = -1;

          FUserEvent user_event(Event::User, id);
          queueEvent (this, &user_event);
        }
      }
    }

    void onUserEvent (FUserEvent* ev) override
    {
      auto color = FColor(ev->getUserId());
      getActiveWindow()->setBackgroundColor(color);
      getActiveWindow()->redraw();
    }

  private:
    std::thread t2{};
    std::promise<void> t2_exit;
    std::future<void> t2_future{t2_exit.get_future()};
};

int main (int argc, char* argv[])
{
  MyApp app(argc, argv);
  FDialog dialog("Dialog", &app);
  dialog.setGeometry (FPoint{25, 5}, FSize{30, 10});
  FLabel label("Colorful thread", &dialog);
  label.setGeometry (FPoint{6, 3}, FSize{17, 3});
  label.setAlignment(Align::Center);
  FButton quit_button("&Quit", &dialog);
  quit_button.setGeometry (FPoint{10, 6}, FSize{10, 1});
  quit_button.addCallback
  (
    "clicked",
    getFApplication(),
    &FApplication::cb_exitApp,
    &dialog
  );
  FWidget::setMainWidget(&dialog);
  dialog.show();

  myTcl2FCEvent ext_event;
  std::promise<void> t1_exit;
  std::future<void> t1_future{t1_exit.get_future()};
  std::thread t1(&Tcl2FCEvent::work, &ext_event, std::move(t1_future));
  int ret = app.exec();
  t1_exit.set_value();  // Stop thread t2
  t1.join();            // Wait until the thread has finished
  return ret;
}

Last modified: Code changes for the new scoped enumeration data types

@davygrvy
Copy link

Thank you for the code example. I've been offline for a while. I built the sample last night, but it's apparent I need to update FC git and rebuild for it to work. Nice. queueEvent() is thread-safe?

@gansm
Copy link
Owner

gansm commented May 26, 2020

I forgot to tell you that the queueEvent() method is of course not thread-safe! You still need to implement your own queue, because FINAL CUT does not support threads at this stage of development. If you follow the design of queueEvent() for your implementation, you should have no problems with future FINAL CUT releases. For a better integration of your code without patching FApplication, I introduced the new virtual method processExternalUserEvent() (a0e1115). You can overload this method and fill it with your own queue code.

(A small application example: Using a user event)

@alavaelian
Copy link

I forgot to tell you that the queueEvent() method is of course not thread-safe! You still need to implement your own queue, because FINAL CUT does not support threads at this stage of development. If you follow the design of queueEvent() for your implementation, you should have no problems with future FINAL CUT releases. For a better integration of your code without patching FApplication, I introduced the new virtual method processExternalUserEvent() (a0e1115). You can overload this method and fill it with your own queue code.

(A small application example: Using a user event)

what is threads ?

@gansm
Copy link
Owner

gansm commented Apr 11, 2022

what is threads ?

I was talking about POSIX threads (pthreads), the parallel processing of different code parts.

@wimstockman
Copy link

Hi Gansm,
I put my little demo app on github.
Feel free to add your comments :-)
receipt_demo
Kind regards
Wim Stockman

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

9 participants