on your control, you will need to handle EVENT_LEFT_CLICK to detect the user pressing the button and EVENT_LEFT_CLICK_UP to detect the user releasing the mouse button. then there are many ways to achieve your goal:
-
the easiest way is to create an asynchronous timer. the timer callback is the body of the loop. start the timer on EVENT_LEFT_CLICK, stop the timer on EVENT_LEFT_CLICK_UP. this method may have some problems since it has a top speed (depending on your processor and the time quantum of threads on your OS) which is slower than just looping, especially if your loop body is very short.
-
the LabView way is to create a state machine, and replace the call to
RunUserInterface() by a loop which first calls
ProcessSystemEvents() then execute your state machine. you switch state on EVENT_LEFT_CLICK and reset your state on EVENT_LEFT_CLICK_UP. a neat way to perform your stuff, also easily expandable with more states. a small disadvantage is that ProcessSystemEvents() may take some time to complete, depending on what you are doing on the user interface (like dragging or resizing a window continuously).
typedef enum
{
idle,
loop,
exit
} STATE;
STATE state;
int main()
{
// initialize CVI runtime engine, load panels, ...
// the following loop replaces the call to RunUserInterface()
while ( state != exit )
{
ProcessSystemEvents();
switch ( state )
{
case idle:
break;
case loop:
// perform the loop body here
break;
}
}
// close panels...
return 0;
}
int CVICALLBACK button_event (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_LEFT_CLICK:
state = loop;
break;
case EVENT_LEFT_CLICK_UP:
state = idle;
break;
}
return 0;
}
-
the dirty way is to create a thread on EVENT_LEFT_CLICK. the thread loops until a global variable is set to a specific value then exits. set the variable to the exit value on EVENT_LEFT_CLICK_UP. this method is dirty because of the need to use a global variable. also a small delay is observed between the click and the start of the thread, which is the time it takes to create, initialize and start the new thread (only a few ms).
#include <windows.h>
static DWORD WINAPI loop_body( LPVOID parameter );
static HANDLE loop_thread;
static int loop_control = 0;
int CVICALLBACK button_event (int panel, int control, int event, void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_LEFT_CLICK:
loop_control = 1;
loop_thread = CreateThread( NULL, 0, &loop_body, &loop_control, 0, NULL ); // sorry NI but i don't buy the thread pool idea: i want my thread to execute NOW
break;
case EVENT_LEFT_CLICK_UP:
loop_control = 0;
WaitForSingleObject( loop_thread, INFINITE );
CloseHandle( loop_thread );
break;
}
return 0;
}
static DWORD WINAPI loop_body( LPVOID parameter )
{
int *control = (int *)parameter;
// if you need some variables local to your loop, declare them here
// perform your initialization here.
while ( *control != 0 )
{
// do whatever you need in the body of the loop...
}
// if you need to release some resources, it's time to do it here
return 0;
}
(- a nicer way: same method as above but encapsulate everything in a proper structure with an initialization function and a dispose function, and a proper synchronisation object to start and end the loop. why all that stuff ? because you may want to have the same behavior on multiple controls, and the code above will force you to copy/paste for each control you need)
there may still be a lot of other ways to do that... be creative !