/* busy.c -- demonstrate how to use a WorkingDialog and to process ** only important events. e.g., those that may interrupt the ** task or to repaint widgets for exposure. Set up a simple shell ** and a widget that, when pressed, immediately goes into its own ** loop. Set a timeout cursor on the shell and pop up a WorkingDialog ** Then enter loop and sleep for one second ten times, checking between ** each interval to see if the user clicked the Stop button or if ** any widgets need to be refreshed. Ignore all other events. ** ** main() and get_busy() are stubs that would be replaced by a real ** application; all other functions can be used as is. */ #include #include #include Widget shell; void TimeoutCursors(Boolean, Boolean); Boolean CheckForInterrupt(void); main (int argc, char *argv[]) { XtAppContext app; Widget button; XmString label; Arg args[2]; void get_busy(Widget, XtPointer, XtPointer); XtSetLanguageProc (NULL, NULL, NULL); shell = XtVaOpenApplication (&app, "Demos", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL); label = XmStringCreateLocalized ("Press To Start A Long Task"); XtSetArg (args[0], XmNlabelString, label); button = XmCreatePushButton (shell, "button", args, 1); XmStringFree (label); XtAddCallback (button, XmNactivateCallback, get_busy, NULL); XtManageChild (button); XtRealizeWidget (shell); XtAppMainLoop (app); } void get_busy (Widget widget, XtPointer client_data, XtPointer call_data) { int n; TimeoutCursors (True, True); for (n = 0; n < 10; n++) { sleep ((unsigned) 1); if (CheckForInterrupt ()) { puts ("Interrupt!"); break; } } if (n == 10) puts ("Done"); TimeoutCursors (False, False); } /* The interesting part of the program -- extract and use at will */ static Boolean stopped; /* True when user wants to stop task */ static Widget dialog; /* WorkingDialog displayed */ /* TimeoutCursors() -- turns on the watch cursor over the application ** to provide feedback for the user that she's going to be waiting ** a while before she can interact with the application again. */ void TimeoutCursors (Boolean on, Boolean interruptable) { static int locked = False; static Cursor cursor = (Cursor) 0; extern Widget shell; XSetWindowAttributes attrs; Display *dpy = XtDisplay (shell); XEvent event; Arg args[5]; int n; XmString str; void stop(Widget, XtPointer, XtPointer); /* "locked" keeps track if we've already called the function. ** This allows recursion and is necessary for most situations. */ if (on) locked++; else locked--; if (locked > 1 || (locked == 1 && on == False)) return; /* already locked and we're not unlocking */ stopped = False; if (!cursor) cursor = XCreateFontCursor (dpy, XC_watch); /* if on is true, then turn on watch cursor, otherwise, return ** the shell's cursor to normal. */ attrs.cursor = on ? cursor : None; /* change the main application shell's cursor to be the timeout ** cursor or to reset it to normal. If other shells exist in ** this application, they will have to be listed here in order ** for them to have timeout cursors too. */ XChangeWindowAttributes (dpy, XtWindow (shell), CWCursor, &attrs); XFlush (dpy); if (on) { /* we're timing out, put up a WorkingDialog. If the process ** is interruptable, allow a "Stop" button. Otherwise, remove ** all actions so the user can't stop the processing. */ n = 0; str = XmStringCreateLocalized ("Busy -- Please Wait."); XtSetArg (args[n], XmNmessageString, str); n++; dialog = XmCreateWorkingDialog (shell, "busy", args, n); XmStringFree (str); XtUnmanageChild (XtNameToWidget (dialog, "OK")); XtUnmanageChild (XtNameToWidget (dialog, "Help")); if (interruptable) { str = XmStringCreateLocalized ("Stop"); XtVaSetValues (dialog, XmNcancelLabelString, str, NULL); XmStringFree (str); XtAddCallback (dialog, XmNcancelCallback, stop, NULL); } else XtUnmanageChild (XtNameToWidget (dialog, "Cancel")); XtManageChild (dialog); } else { /* get rid of all button and keyboard events that occured ** during the time out. The user shouldn't have done anything ** during this time, so flush for button and keypress events. ** KeyRelease events are not discarded because accelerators ** require the corresponding release event before normal input ** can continue. */ while (XCheckMaskEvent (dpy, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask | KeyPressMask, &event)) { /* do nothing */; } XtDestroyWidget (dialog); } } /* stop() -- user pressed the "Stop" button in dialog. */ void stop (Widget dialog, XtPointer client_data, XtPointer call_data) { stopped = True; } /* CheckForInterrupt() -- check events in event queue and process ** the interesting ones. */ Boolean CheckForInterrupt (void) { extern Widget shell; Display *dpy = XtDisplay (shell); Window win = XtWindow (dialog); XEvent event; /* Make sure all our requests get to the server */ XFlush (dpy); /* Let Motif process all pending exposure events for us. */ XmUpdateDisplay (shell); /* Check the event loop for events in the dialog ("Stop"?) */ while (XCheckMaskEvent (dpy, ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | PointerMotionMask | KeyPressMask, &event)) { /* got an "interesting" event. */ if (event.xany.window == win) XtDispatchEvent (&event); /* it's in our dialog.. */ else /* uninteresting event--throw it away and sound bell */ XBell (dpy, 50); } return stopped; }