/* $TOG: piano.c /main/11 1997/05/14 13:42:25 bill $ */ /* * @OPENGROUP_COPYRIGHT@ * COPYRIGHT NOTICE * Copyright (c) 1990, 1991, 1992, 1993 Open Software Foundation, Inc. * Copyright (c) 1996, 1997, 1998, 1999, 2000 The Open Group * ALL RIGHTS RESERVED (MOTIF). See the file named COPYRIGHT.MOTIF for * the full copyright text. * * This software is subject to an open license. It may only be * used on, with or for operating systems which are themselves open * source systems. You must contact The Open Group for a license * allowing distribution and sublicensing of this software on, with, * or for operating systems which are not Open Source programs. * * See http://www.opengroup.org/openmotif/license for full * details of the license agreement. Any use, reproduction, or * distribution of the program constitutes recipient's acceptance of * this agreement. * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS * PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY * WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY * OR FITNESS FOR A PARTICULAR PURPOSE * * EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT * NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE * EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGES. * */ /* * HISTORY */ /**************************************************************************** **************************************************************************** ** ** File: piano.c ** ** Version: 2.0 ** ** By: Andrew deBlois ** ** This application won't be able to play tunes on the pmax and sun ** since you can't change the tones generated by XBell. ** **************************************************************************** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "piano.images" /* note that anything after REST must be some type of rest. */ typedef enum {EIGHTH, EIGHTHDOT, EIGHTHSHARP, EIGHTHDOTSHARP, QUARTER, QUARTERDOT, QUARTERSHARP, QUARTERDOTSHARP, HALF, HALFDOT, HALFSHARP, HALFDOTSHARP, REST, RESTDOT, LAST_NOTE} NoteType; char *noteName[] = {"eighth", "eighthdot", "eighthsharp", "eighthdotsharp", "quarter", "quarterdot", "quartersharp", "quarterdotsharp", "half", "halfdot", "halfsharp", "halfdotsharp", "rest", "restdot"}; typedef enum { MENU_QUIT, MENU_HELP } MenuFunction; /*-------------------------------------------------------------* | Types | *-------------------------------------------------------------*/ /***** * NoteDescription: Structure to hold the description of all note types supported. */ typedef struct _NoteDescription { Pixmap image; Pixmap mask; } NoteDescription; /***** * NoteRec: Data stored in each note after it is added to the staff. */ typedef struct _NoteRec { Display *display; NoteType noteType; /* type of note or rest. */ int noteDuration; int noteNumber; /* number of note to play */ int noteIndex; /* index used for positioning. */ int ledgerLine; struct _NoteRec *next; } NoteRec; /***** * StaffRec: Data used to manipulate the staff containing a list of notes. */ typedef struct _StaffRec { Display *display; Widget staff; NoteRec *notes; Widget divider; /* seperator above this staff. */ struct _StaffRec *prev, *next; } StaffRec; /***** * AppData: Data used throughout the app to hold all necessary data. */ typedef struct _AppData { /* resources */ int baseDuration; /* equal to one quarter note */ float baseFrequency; /* frequency assigned to new staffs. */ Boolean useKeyboard; /* if true, keyboard played with voice(s). */ int wkeyCount; /* number of white keys on the keyboard. */ int keyHeight; /* white key keight - also sets black keys */ int keyWidth; /* width of each black key. */ /* data */ GC noteGC; NoteType activeNoteType; /* index into the noteTable */ NoteDescription noteTable[LAST_NOTE]; /* definition info for each note type. */ StaffRec *staffList; /* holds list of all staffs (voices). */ Widget score; /* the rowcolumn holding the staffs. */ } AppData; /*-------------------------------------------------------------* | defines | *-------------------------------------------------------------*/ #define APP_NAME "piano" #define APP_CLASS "Piano" /* * default resource settings. */ #define DEFAULT_BASE_FREQUENCY "246.9413" #define DEFAULT_BASE_DURATION 200 #define DEFAULT_WKEY_COUNT 28 /* number of white keys */ #define DEFAULT_KEY_HEIGHT 160 #define DEFAULT_KEY_WIDTH 20 #define LOCAL_NAME "local" /* just used for a label */ #define EMSG1 "Fatal Error -- Cannot allocate memory for resources.\n" /*-------------------------------------------------------------* | Resources | *-------------------------------------------------------------*/ XtResource appRes[] = { {"baseDuration", "BaseDuration", XtRInt, sizeof(int), XtOffsetOf(AppData, baseDuration), XtRImmediate, (XtPointer)DEFAULT_BASE_DURATION}, {"baseFrequency", "BaseFrequency", XtRFloat, sizeof(float), XtOffsetOf(AppData, baseFrequency), XmRString, DEFAULT_BASE_FREQUENCY}, {"useKeyboard", "UseKeyboard", XtRBoolean, sizeof(Boolean), XtOffsetOf(AppData, useKeyboard), XmRImmediate, (XtPointer)TRUE}, {"wkeyCount", "WkeyCount", XtRInt, sizeof(int), XtOffsetOf(AppData, wkeyCount), XtRImmediate, (XtPointer)DEFAULT_WKEY_COUNT}, {"keyHeight", "KeyHeight", XtRInt, sizeof(int), XtOffsetOf(AppData, keyHeight), XtRImmediate, (XtPointer)DEFAULT_KEY_HEIGHT}, {"keyWidth", "KeyWidth", XtRInt, sizeof(int), XtOffsetOf(AppData, keyWidth), XtRImmediate, (XtPointer)DEFAULT_KEY_WIDTH}, }; /*-------------------------------------------------------------* | Function Declarations | *-------------------------------------------------------------*/ /* KEYBOARD */ void BuildKeys (Widget); Widget CreateKeyboard (Widget); /* SCORE */ StaffRec *GetStaffData (Widget); void DrawNotes (Widget, int, int); void DrawStaffCB (Widget, XtPointer, XtPointer); void SetIcon (Widget, Pixmap); void DrawNote (Widget, NoteRec *, int, int); void SetActiveNote (Widget, NoteType); void AddNoteAtPosn (Widget, int, NoteType); void AddNoteToStaffCB (Widget, XtPointer, XtPointer); void AddNewStaff (Display *, char *); void PostStaffMenu (Widget, XtPointer, XEvent *, Boolean *); void CreateStaffMenu (Widget, Widget, char *); /* OTHER */ void AddVoiceCB (Widget, XtPointer, XtPointer); void RemoveVoiceCB (Widget, XtPointer, XtPointer); void ClearVoiceCB (Widget, XtPointer, XtPointer); void PlayVoiceCB (Widget, XtPointer, XtPointer); void PlayAllCB (Widget, XtPointer, XtPointer); void SetAppIcon (Widget); void GetBell (Display *); void SetBell (Display *, int, int); int Pitch (int); void PlayNote (XtPointer, XtIntervalId *); void SoundCB (Widget, XtPointer, XtPointer); void SetNoteCB (Widget, XtPointer, XtPointer); void CreateScore (Widget); Widget CreateNotebook (Widget); void CvtStrToFloat (XrmValue *, Cardinal *, XrmValue *, XrmValue *); /* Globals */ AppData *appData; XtAppContext context; int orig_percent, orig_pitch, orig_duration; Widget key[1000]; String fallback[] = { "Piano*highlightThickness: 0", "Piano*borderWidth: 0", "Piano*iconImage: Piano.bmp", "Piano*borderWidth: 0", "Piano*margin: 0", "Piano*wKey.background: white", "Piano*bKey.background: black", "Piano*wKey.foreground: black", "Piano*bKey.foreground: white", "Piano*wKey.shadowThickness: 2", "Piano*bKey.shadowThickness: 4", "Piano*wKey.armColor: grey85", "Piano*bKey.armColor: grey0", "Piano*wKey.topShadowColor: white", "Piano*bKey.bottomShadowColor: black", "Piano*wKey.topShadowColor: grey60", "Piano*bKey.bottomShadowColor: grey20", "Piano*bKey.labelString: ", "Piano*wKey.labelString: ", "Piano*keyboard.marginWidth: 10", "Piano*keyboard.marginHeight: 10", "Piano*scoreWin.height: 111", "Piano*staff.width: 920", "Piano*staff.height: 100", "Piano*popupBtn1.labelString: Add Voice", "Piano*popupBtn2.labelString: Remove Voice", "Piano*popupBtn3.labelString: Clear Voice", "Piano*popupBtn4.labelString: Play Voice", "Piano*popupBtn5.labelString: Play All", "Piano*popupBtn6.labelString: Save Voice", "Piano*popupBtn7.labelString: Load Voice", "Piano*cascade1.labelString: File", "Piano*cascade2.labelString: Help", "Piano*b1.labelString: Quit", "Piano*notebook.orientation: horizontal", "Piano*notebook.adjustLast: false", "Piano*notebook*paneMaximum: 40", "Piano*dspPromptDlog.labelString: Enter name of display to connect to:", "Piano*warnDlog.messageString: Error in connecting to display", "Piano*helpDlog*messageString:\ Piano Demo\\n\ ----------\\n\ Press Btn3 on a staff to post an associated menu\\n\ containing the following items:\\n\ Add Voice - Add a new staff and voice. Each voice may\\n\ connect to a different display.\\n\ Remove Voice - Removes a staff and voice from the score.\\n\ Clear Voice - Removes all notes in a staff.\\n\ Play Voice - Plays the notes in the selected staff.\\n\ Play All - Plays all voices in the score together.\\n\ Save Voice - Saves the selected voice to a file.\\n\ Load Voice - Loads a voice from a file. This will\\n\ append to any existing notes in the voice.\\n\\n\ To delete a note, press Btn2 over the desired note in a staff.\\n\\n\ Settable resources are:\\n\ baseDuration - sets the duration of a quarter note. (msec)\\n\ baseFrequency - sets the frequency of bottom note. (Hz)\\n\ useKeyboard - specifies if keyboard should play along.\\n\ wkeyCount - number of white keys on the keyboard.\\n\ keyHeight - initial height in pixels of the white keys.\\n\ keyWidth - initial width in pixels of the white keys.", NULL }; /***********************************************************************/ /*----------------------------------------------------------------* | MyErrorHandler | *----------------------------------------------------------------*/ int MyErrorHandler (Display *display, XErrorEvent *errorEvent) { /* this is most likely invoked when the frequency selected is out of range. */ printf("X Error!\n"); return 0; /* Ignored by X. */ } /*----------------------------------------------------------------* | DoQuit | *----------------------------------------------------------------*/ void DoQuit () { exit(0); } /*----------------------------------------------------------------* | DoHelp | *----------------------------------------------------------------*/ void DoHelp () { static Widget dlog = NULL; Arg args[3]; int n; if (dlog == NULL) { dlog = XmCreateInformationDialog(appData->score, "helpDlog", NULL, 0); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_HELP_BUTTON) ); XtUnmanageChild( XmMessageBoxGetChild (dlog, XmDIALOG_CANCEL_BUTTON) ); } XtManageChild(dlog); } /*----------------------------------------------------------------* | MenuCB | *----------------------------------------------------------------*/ void MenuCB (Widget w, XtPointer clientData, XtPointer callData) { switch ((long)clientData) { case MENU_QUIT: DoQuit(); break; case MENU_HELP: DoHelp(); break; } } /*--------------------------------------------------------------------* | DoAddVoiceCB | *--------------------------------------------------------------------*/ void DoAddVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { XmSelectionBoxCallbackStruct *cb = (XmSelectionBoxCallbackStruct *)callData; String dspName; Display *newDisplay; String appName, appClass; static Widget dlog = NULL; int n, argc = 0; Arg args[5]; XtGetApplicationNameAndClass(XtDisplay(w), &appName, &appClass); XmStringGetLtoR(cb->value, XmSTRING_DEFAULT_CHARSET, &dspName); newDisplay = XtOpenDisplay(context, dspName, appName, appClass, NULL, 0, &argc, NULL); if (newDisplay != NULL) AddNewStaff(newDisplay, dspName); else { if (dlog == NULL) { n = 0; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; XtSetArg(args[n], XmNdialogType, XmDIALOG_ERROR); n++; dlog = XmCreateWarningDialog(appData->score, "warnDlog", args, n); } XtManageChild(dlog); } if (dspName) XtFree(dspName); } /*--------------------------------------------------------------------* | AddVoiceCB | *--------------------------------------------------------------------*/ void AddVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Cardinal n; Arg args[5]; static Widget dlog = NULL; if (dlog == NULL) { n = 0; XtSetArg(args[n], XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL); n++; XtSetArg(args[n], XmNdialogType, XmDIALOG_PROMPT); n++; dlog = XmCreatePromptDialog(appData->score, "dspPromptDlog", args, n); XtAddCallback(dlog, XmNokCallback, DoAddVoiceCB, NULL); } XtManageChild(dlog); } /*--------------------------------------------------------------------* | RemoveVoiceCB | *--------------------------------------------------------------------*/ void RemoveVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Widget staff = (Widget)clientData; StaffRec *staffData; staffData = GetStaffData(staff); if (staffData->divider != NULL) XtDestroyWidget(staffData->divider); XtDestroyWidget(staff); if (staffData->next != NULL) staffData->next->prev = staffData->prev; if (staffData->prev != NULL) staffData->prev->next = staffData->next; else appData->staffList = staffData->next; XtFree((char *)staffData); } /*--------------------------------------------------------------------* | ClearVoiceCB | *--------------------------------------------------------------------*/ void ClearVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Widget staff = (Widget)clientData; StaffRec *staffData; NoteRec *notes, *np; staffData = GetStaffData(staff); if (staffData != NULL) { while (staffData->notes != NULL) { np = staffData->notes; staffData->notes = staffData->notes->next; XtFree((char *)np); } XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE); } } /*--------------------------------------------------------------------* | ArmKey | *--------------------------------------------------------------------*/ void ArmKey (XtPointer clientData, XtIntervalId *id) { Widget key = (Widget) clientData; XEvent event; XtCallbackList cbList; XtVaGetValues(key, XmNarmCallback, &cbList, NULL); XtVaSetValues(key, XmNarmCallback, NULL, NULL); XtCallActionProc(key, "Arm", &event, NULL, 0); XtVaSetValues(key, XmNarmCallback, cbList, NULL); } /*--------------------------------------------------------------------* | DisarmKey | *--------------------------------------------------------------------*/ void DisarmKey (XtPointer clientData, XtIntervalId *id) { Widget key = (Widget) clientData; XEvent event; XtCallActionProc(key, "Disarm", &event, NULL, 0); } /*--------------------------------------------------------------------* | PlayNotes | | Note that bell duration and interval timing are defined in X as | | based on milliseconds. Add a timeout for each note to play, then | | let things go. | *--------------------------------------------------------------------*/ void PlayNotes (XtPointer clientData, XtIntervalId *id) { NoteRec *note = (NoteRec *)clientData; XEvent event; XtCallbackList tempCallbackList; int dt = 0; while (note != NULL) { if (note->noteType < REST) { XtAppAddTimeOut(context, dt, PlayNote, note); /* now to press the keys. */ if (appData->useKeyboard) { XtAppAddTimeOut(context, dt, ArmKey, key[note->noteNumber]); XtAppAddTimeOut(context, dt+note->noteDuration, DisarmKey, key[note->noteNumber]); } } dt += note->noteDuration; note = note->next; } } /*--------------------------------------------------------------------* | PlayVoiceCB | *--------------------------------------------------------------------*/ void PlayVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Widget staff = (Widget)clientData; StaffRec *staffData; NoteRec *notes; staffData = GetStaffData(staff); if (staffData != NULL) XtAppAddTimeOut(context, 1, PlayNotes, staffData->notes); } /*--------------------------------------------------------------------* | PlayAllCB | *--------------------------------------------------------------------*/ void PlayAllCB (Widget staff, XtPointer clientData, XtPointer callData) { StaffRec *sPtr; for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next) { XtAppAddTimeOut(context, 1, PlayNotes, sPtr->notes); } } /*--------------------------------------------------------------------* | DoSaveVoiceCB | *--------------------------------------------------------------------*/ void DoSaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { XmFileSelectionBoxCallbackStruct *fdata = (XmFileSelectionBoxCallbackStruct *)callData; Widget staff = (Widget)clientData; StaffRec *staffData; NoteRec *note; FILE *fp; char *fileName; static Widget errDlog = NULL; if (fdata->length > 0) { XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName); staffData = GetStaffData(staff); if (staffData != NULL) { fp = fopen(fileName, "w"); for (note = staffData->notes; note != NULL; note = note->next) { fprintf(fp, "%d %d %d %d %d\n", note->noteType, note->noteDuration, note->noteNumber, note->noteIndex, note->ledgerLine); } fclose(fp); } if (fileName) XtFree(fileName); } } /*--------------------------------------------------------------------* | SaveVoiceCB | *--------------------------------------------------------------------*/ void SaveVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Widget staff = (Widget)clientData; static Widget fsdlog = NULL; static Widget oldStaff; if (fsdlog == NULL) { fsdlog = XmCreateFileSelectionDialog(staff, "saveDlog", NULL, 0); XtVaSetValues(fsdlog, XmNautoUnmanage, True, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, NULL); } else { XtRemoveCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, oldStaff); } XtAddCallback(fsdlog, XmNokCallback, DoSaveVoiceCB, staff); oldStaff = staff; XtManageChild(fsdlog); } /*--------------------------------------------------------------------* | DoLoadVoiceCB | *--------------------------------------------------------------------*/ void DoLoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { XmFileSelectionBoxCallbackStruct *fdata = (XmFileSelectionBoxCallbackStruct *)callData; Widget staff = (Widget)clientData; StaffRec *staffData; NoteRec *note, *tail; FILE *fp; Boolean done = FALSE; int noteOffset; char *fileName; if (fdata->length > 0) { XmStringGetLtoR(fdata->value, XmSTRING_DEFAULT_CHARSET, &fileName); fp = fopen(fileName, "r"); if (fileName) XtFree(fileName); if (fp != NULL) { staffData = GetStaffData(staff); if (staffData->notes == NULL) { tail = NULL; noteOffset = 0; } else for (tail=staffData->notes, noteOffset = 1; tail->next != NULL; tail = tail->next, noteOffset++); while (!done) { note = (NoteRec *) XtMalloc(sizeof(NoteRec)); if (fscanf(fp, "%d %d %d %d %d\n", ¬e->noteType, ¬e->noteDuration, ¬e->noteNumber, ¬e->noteIndex, ¬e->ledgerLine) > 0) { note->noteIndex += noteOffset; note->display = staffData->display; note->next = NULL; if (tail == NULL) { staffData->notes = note; tail = note; } else { tail->next = note; tail = tail->next; } } else { XtFree((XtPointer)note); done = TRUE; } } fclose(fp); } XClearArea(XtDisplay(w), XtWindow(staff), 0, 0, 0, 0, TRUE); } } /*--------------------------------------------------------------------* | LoadVoiceCB | *--------------------------------------------------------------------*/ void LoadVoiceCB (Widget w, XtPointer clientData, XtPointer callData) { Widget staff = (Widget)clientData; static Widget fsdlog = NULL; static Widget oldStaff; if (fsdlog == NULL) { fsdlog = XmCreateFileSelectionDialog(staff, "loadDlog", NULL, 0); XtVaSetValues(fsdlog, XmNautoUnmanage, True, XmNdialogStyle, XmDIALOG_FULL_APPLICATION_MODAL, NULL); } else { XtRemoveCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, oldStaff); } XtAddCallback(fsdlog, XmNokCallback, DoLoadVoiceCB, staff); oldStaff = staff; XtManageChild(fsdlog); } /*--------------------------------------------------------------------* | DrawNotes | | x1,x2 specify the clipping width. If they are the same value, no | | clipping is performed. | *--------------------------------------------------------------------*/ void DrawNotes (Widget staff, int x1, int x2) { StaffRec *staffData; NoteRec *notes, *np; staffData = GetStaffData(staff); if (staffData != NULL) for (np = staffData->notes; np != NULL; np = np->next) DrawNote(staff, np, x1, x2); } /*--------------------------------------------------------------------* | DrawStaffCB | *--------------------------------------------------------------------*/ void DrawStaffCB (Widget staff, XtPointer clientData, XtPointer callData) { XExposeEvent *expEvt = (XExposeEvent *)((XmDrawingAreaCallbackStruct*)callData)->event; int i, y; Dimension width, height; if (expEvt->count > 1) return; XtVaGetValues(XtParent(staff), XmNwidth, &width, NULL); XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL); for (i=4; i<=12; i+=2) { y = i*(int)height / 16; XDrawLine(XtDisplay(staff), XtWindow(staff), DefaultGCOfScreen(XtScreen(staff)), 0, y, width, y); } DrawNotes(staff, expEvt->x, expEvt->x + expEvt->width); } /*-------------------------------------------------------------* | SetAppIcon() | *-------------------------------------------------------------*/ void SetAppIcon(Widget shell) { Pixmap iconPixmap; iconPixmap = XCreateBitmapFromData(XtDisplay(shell), XtScreen(shell)->root, (char*)piano_bits, piano_width, piano_height); XtVaSetValues(shell, XmNiconPixmap, iconPixmap, NULL); } /*--------------------------------------------------------------------* | GetBell | *--------------------------------------------------------------------*/ void GetBell(Display *dpy) { XKeyboardState stateValues; XGetKeyboardControl(dpy, &stateValues); orig_percent = stateValues.bell_percent; orig_pitch = stateValues.bell_pitch; orig_duration = stateValues.bell_duration; } /*--------------------------------------------------------------------* | SetBell | *--------------------------------------------------------------------*/ void SetBell(Display *dpy, int pitch, int duration) { XKeyboardControl controlValues; unsigned long valueMask = KBBellPercent | KBBellPitch | KBBellDuration; controlValues.bell_percent = orig_percent; controlValues.bell_pitch = pitch; controlValues.bell_duration = duration; XChangeKeyboardControl(dpy, valueMask, &controlValues); } /*--------------------------------------------------------------------* | Pitch | *--------------------------------------------------------------------*/ int Pitch (int note) { double x, m, n, f; /* notes are calculated from the base frequency. */ /* This is the first note on the keyboard. */ /* The frequency of a note = 2^(index / 12). */ x = (double)2.0; m = (double)note; n = (double)12.0; f = (double)appData->baseFrequency * pow(x, (m/n)); return((int)f); } /*--------------------------------------------------------------------* | PlayNote | *--------------------------------------------------------------------*/ void PlayNote (XtPointer clientData, XtIntervalId *id) { NoteRec *note = (NoteRec *)clientData; SetBell(note->display, Pitch(note->noteNumber), note->noteDuration); XBell(note->display, 100); SetBell(note->display, orig_pitch, orig_duration); } /*--------------------------------------------------------------------* | SoundCB | *--------------------------------------------------------------------*/ void SoundCB (Widget w, XtPointer noteNumber, XtPointer callData) { XmPushButtonCallbackStruct *cb = (XmPushButtonCallbackStruct *)callData; NoteRec note; /* only play the note if this is a true arm event. */ if (cb->event != NULL) { note.display = XtDisplay(w); note.noteType = EIGHTH; note.noteNumber = (NoteType)noteNumber; note.noteIndex = 0; note.ledgerLine = 0; note.noteDuration = appData->baseDuration; PlayNote(¬e, 0); } } #define MUG_SHOTS 11 /*--------------------------------------------------------------------* | BuildKeys | *--------------------------------------------------------------------*/ void BuildKeys (Widget parent) { Pixmap iconPixmaps[MUG_SHOTS+1]; int i, j = 0, imageCount=MUG_SHOTS; Boolean pixmapsSet = FALSE, easterEgg = False; int noteCount = appData->wkeyCount; static Boolean firstTime = True; if (firstTime) { int x, y, junk; unsigned int bjunk; Window wjunk; firstTime = False; /* dev team's signature... :-) */ XQueryPointer(XtDisplay(parent), RootWindowOfScreen(XtScreen(parent)), &wjunk, &wjunk, &x, &y, &junk, &junk, &bjunk); easterEgg = (x+y == 0); if (easterEgg) { Display *dsp = XtDisplay(parent); Window win = RootWindowOfScreen(XtScreen(parent)); Pixmap shapemask; XpmAttributes attributes; attributes.valuemask = 0; XmeXpmCreatePixmapFromData(dsp, win, none, &(iconPixmaps[0]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, andrew, &(iconPixmaps[1]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, dan, &(iconPixmaps[2]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, dave, &(iconPixmaps[3]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, doug, &(iconPixmaps[4]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, ellis, &(iconPixmaps[5]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, ingeborg, &(iconPixmaps[6]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, jim, &(iconPixmaps[7]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, kamesh, &(iconPixmaps[8]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, scott, &(iconPixmaps[9]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, steve, &(iconPixmaps[10]), &shapemask, &attributes); XmeXpmCreatePixmapFromData(dsp, win, vania, &(iconPixmaps[11]), &shapemask, &attributes); appData->wkeyCount = noteCount = imageCount; } } XtVaSetValues(parent, XmNfractionBase, noteCount * 10, NULL); j = 0; for (i=0; ikeyWidth, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, i*10 + 7, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, i*10 + 13, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_POSITION, XmNbottomPosition, noteCount*6, NULL); XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j); } j = 0; for (i=0; ikeyHeight, XmNleftAttachment, XmATTACH_POSITION, XmNleftPosition, i*10, XmNrightAttachment, XmATTACH_POSITION, XmNrightPosition, (i+1)*10, XmNtopAttachment, XmATTACH_FORM, XmNbottomAttachment, XmATTACH_FORM, NULL); XtAddCallback(key[j], XmNarmCallback, SoundCB, (XtPointer)(long)j); if (easterEgg) { XtVaSetValues(key[j], XmNlabelType, XmPIXMAP, XmNlabelPixmap, iconPixmaps[0], XmNarmPixmap, iconPixmaps[(i%imageCount)+1], XmNmarginLeft, 0, XmNmarginRight, 0, XmNmarginTop, 100, NULL); } } } /*--------------------------------------------------------------------* | CreateKeyboard | *--------------------------------------------------------------------*/ Widget CreateKeyboard(Widget parent) { int i, j = 0; Widget keyBoard, wKey, bKey; keyBoard = XtVaCreateWidget("keyBoard", xmFormWidgetClass, parent, NULL); BuildKeys(keyBoard); XtManageChild(keyBoard); return(keyBoard); } /*--------------------------------------------------------------------* | SetIcon | *--------------------------------------------------------------------*/ void SetIcon (Widget w, Pixmap cursorPixmap) { Pixel fgPix, bgPix; Cursor cursor; XColor xcolors[2]; Display *dsp = XtDisplay(w); xcolors[0].pixel = BlackPixelOfScreen(DefaultScreenOfDisplay(dsp)); xcolors[1].pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(dsp)); XQueryColors(dsp, DefaultColormapOfScreen(DefaultScreenOfDisplay(dsp)), xcolors, 2); cursor = XCreatePixmapCursor(dsp, cursorPixmap, cursorPixmap, &(xcolors[0]), &(xcolors[1]), note_x_hot, note_y_hot); XDefineCursor(dsp, XtWindow(w), cursor); } /*--------------------------------------------------------------------* | GetStaffData | | This scans the list of staffs for a match. It returns the data | | associated with the staff. | *--------------------------------------------------------------------*/ StaffRec *GetStaffData (Widget staff) { StaffRec *sPtr; for (sPtr = appData->staffList; sPtr != NULL; sPtr = sPtr->next) { if (sPtr->staff == staff) return (sPtr); } /* should never get here */ return (NULL); } /*--------------------------------------------------------------------* | DrawNote | *--------------------------------------------------------------------*/ void DrawNote (Widget staff, NoteRec *note, int x1, int x2) { Dimension width, height; Pixmap notePix, notePixMask; int x, y; notePix = appData->noteTable[note->noteType].image; notePixMask = appData->noteTable[note->noteType].mask; XtVaGetValues(staff, XmNwidth, &width, XmNheight, &height, NULL); x = note->noteIndex * 15; y = (15 - note->ledgerLine) * (int)height / 16 - (note_height/2) - 4; /* if the position is off the right side of the staff, resize it. */ if ((x + note_width) > (int)width) XtVaSetValues(staff, XmNwidth, x + 2*note_width, NULL); if ((x1 != x2) && (x < x1-note_width || x > x2+note_width)) return; XSetClipMask (XtDisplay(staff), appData->noteGC, notePixMask); XSetClipOrigin(XtDisplay(staff), appData->noteGC, x, y); XCopyArea(XtDisplay(staff), notePix, XtWindow(staff), appData->noteGC, 0, 0, note_width, note_height, x, y); } /*--------------------------------------------------------------------* | SetActiveNote | *--------------------------------------------------------------------*/ void SetActiveNote (Widget w, NoteType noteType) { appData->activeNoteType = noteType; SetIcon(appData->score, appData->noteTable[noteType].mask); XSetClipMask(XtDisplay(w), appData->noteGC, appData->noteTable[noteType].mask); } /*--------------------------------------------------------------------* | SetNoteCB | | callback which sets the active note and modifies the cursor. | *--------------------------------------------------------------------*/ void SetNoteCB (Widget w, XtPointer clientData, XtPointer callData) { NoteType noteType = (NoteType)clientData; SetActiveNote(w, noteType); } /*--------------------------------------------------------------------* | NoteNumber | *--------------------------------------------------------------------*/ int NoteNumber (int ledgerLine, Boolean isASharp) { int n = 0; switch (ledgerLine) { case 1: n = 1; break; case 2: n = 3; break; case 3: n = 5; break; case 4: n = 6; break; case 5: n = 8; break; case 6: n = 10; break; case 7: n = 12; break; case 8: n = 13; break; case 9: n = 15; break; case 10: n = 17; break; case 11: n = 18; break; case 12: n = 20; break; } if (isASharp) return (n+1); else return (n); } /*--------------------------------------------------------------------* | DeleteNoteAtPosn | *--------------------------------------------------------------------*/ void DeleteNoteAtPosn (Widget staff, int x, int y) { int ledgerLine, noteIndex, i; Dimension height; StaffRec *staffData; NoteRec *np, *npTemp; /* find the corresponding ledger line in the staff. */ XtVaGetValues(staff, XmNheight, &height, NULL); ledgerLine = 15 - (16 * y / (int)height); noteIndex = x / 15; staffData = GetStaffData(staff); if ((staffData != NULL) && (staffData->notes != NULL)) { if (noteIndex == 1) { npTemp = staffData->notes; staffData->notes = staffData->notes->next; } else { for (np = staffData->notes; ((noteIndex > 2) && (np->next != NULL)); noteIndex--) np = np->next; if (np->next != NULL) { npTemp = np->next; np->next = np->next->next; } else npTemp = NULL; } if (npTemp != NULL) XtFree((XtPointer)npTemp); for (np = staffData->notes, i = 0; np != NULL; np = np->next) np->noteIndex = ++i; XClearArea(XtDisplay(staff), XtWindow(staff), 0, 0, 0, 0, TRUE); } } /*--------------------------------------------------------------------* | AddNoteAtPosn | *--------------------------------------------------------------------*/ void AddNoteAtPosn (Widget staff, int y, NoteType noteType) { int ledgerLine, noteCount, noteDuration; Dimension height; StaffRec *staffData; NoteRec *noteList, *currentNoteList, *np; Boolean isASharp = FALSE; /* find the corresponding ledger line in the staff. */ XtVaGetValues(staff, XmNheight, &height, NULL); ledgerLine = 15 - (16 * y / (int)height); /* round up to G and down to middle C. */ if (ledgerLine < 1) ledgerLine = 1; else if (ledgerLine > 12) ledgerLine = 12; switch (noteType) { case EIGHTH: noteDuration = appData->baseDuration; break; case EIGHTHDOT: noteDuration = appData->baseDuration*3/2; break; case EIGHTHSHARP: noteDuration = appData->baseDuration; isASharp = TRUE; break; case EIGHTHDOTSHARP: noteDuration = appData->baseDuration*3/2; isASharp = TRUE; break; case QUARTER: noteDuration = appData->baseDuration*2; break; case QUARTERDOT: noteDuration = appData->baseDuration*3; break; case QUARTERSHARP: noteDuration = appData->baseDuration*2; isASharp = TRUE; break; case QUARTERDOTSHARP: noteDuration = appData->baseDuration*3; isASharp = TRUE; break; case HALF: noteDuration = appData->baseDuration*4; break; case HALFDOT: noteDuration = appData->baseDuration*6; break; case HALFSHARP: noteDuration = appData->baseDuration*4; isASharp = TRUE; break; case HALFDOTSHARP: noteDuration = appData->baseDuration*6; isASharp = TRUE; break; case REST: noteDuration = appData->baseDuration; break; case RESTDOT: noteDuration = appData->baseDuration*3/2; break; default: noteDuration = 0; } /* get the staff info - this tells the display to use. */ staffData = GetStaffData(staff); noteList = (NoteRec *)XtMalloc(sizeof(NoteRec)); noteList->display = staffData->display; noteList->noteType = noteType; noteList->noteDuration = noteDuration; noteList->noteNumber = NoteNumber(ledgerLine, isASharp); noteList->ledgerLine = ledgerLine; noteList->next = NULL; /* get the current list of notes. */ currentNoteList = staffData->notes; /* find out how many there are. */ for (noteCount=1, np=currentNoteList; ((np != NULL) && (np->next != NULL)); np=np->next, noteCount++) ; if (np == NULL) { staffData->notes = noteList; noteList->noteIndex = noteCount; } else { np->next = noteList; noteList->noteIndex = noteCount+1; } DrawNote(staff, noteList, 0, 0); } /*--------------------------------------------------------------------* | AddNoteToStaffCB | *--------------------------------------------------------------------*/ void AddNoteToStaffCB (Widget staff, XtPointer clientData, XtPointer callData) { XmDrawingAreaCallbackStruct *cb = (XmDrawingAreaCallbackStruct *)callData; XButtonEvent *btnEvent = (XButtonEvent *)cb->event; int vposn, hposn; if ((btnEvent->button == Button1) && (btnEvent->type == ButtonPress)) { AddNoteAtPosn(staff, btnEvent->y, appData->activeNoteType); } else if ((btnEvent->button == Button2) && (btnEvent->type == ButtonPress)) { DeleteNoteAtPosn(staff, btnEvent->x, btnEvent->y); } } /*--------------------------------------------------------------------* | AddNewStaff | | Creates data for a new staff and adds it to the global score. | | A popup menu is attached to the staff. | *--------------------------------------------------------------------*/ void AddNewStaff (Display *newDisplay, char *dspName) { StaffRec *staffData; staffData = (StaffRec *) XtMalloc(sizeof(StaffRec)); staffData->display = newDisplay; staffData->notes = NULL; staffData->next = staffData->prev = NULL; /* if this is not the first staff, the add a seperator. */ if (appData->staffList != NULL) staffData->divider = XtVaCreateManagedWidget("divider", xmSeparatorWidgetClass, appData->score, NULL); else staffData->divider = NULL; staffData->staff = XtVaCreateManagedWidget("staff", xmDrawingAreaWidgetClass, appData->score, XmNresizePolicy, XmRESIZE_NONE, NULL); XtAddCallback(staffData->staff, XmNexposeCallback, DrawStaffCB, staffData); XtAddCallback(staffData->staff, XmNinputCallback, AddNoteToStaffCB, staffData); CreateStaffMenu(appData->score, staffData->staff, dspName); /* * add the staff to the staff list. */ staffData->next = appData->staffList; if (appData->staffList != NULL) appData->staffList->prev = staffData; appData->staffList = staffData; } /*--------------------------------------------------------------------* | PostStaffMenu | *--------------------------------------------------------------------*/ void PostStaffMenu (Widget w, XtPointer clientData, XEvent *event, Boolean *dispatch) { Widget menu = (Widget)clientData; XButtonEvent *btnEvent = (XButtonEvent *)event; int button; XtVaGetValues(menu, XmNwhichButton, &button, NULL); if (btnEvent->button == button) { XmMenuPosition(menu, btnEvent); XtManageChild(menu); } } /*--------------------------------------------------------------------* | CreateStaffMenu | *--------------------------------------------------------------------*/ void CreateStaffMenu (Widget score, Widget staff, char *dspName) { Widget popupMenu, popupBtn[8]; popupMenu = XmCreatePopupMenu(staff, "popupMenu", NULL, 0); XtAddEventHandler(staff, ButtonPressMask, False, PostStaffMenu, popupMenu); XtVaCreateManagedWidget(dspName, xmLabelWidgetClass, popupMenu, NULL); XtVaCreateManagedWidget("line", xmSeparatorWidgetClass, popupMenu, NULL); popupBtn[1] = XtVaCreateManagedWidget("popupBtn1", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[2] = XtVaCreateManagedWidget("popupBtn2", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[3] = XtVaCreateManagedWidget("popupBtn3", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[4] = XtVaCreateManagedWidget("popupBtn4", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[5] = XtVaCreateManagedWidget("popupBtn5", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[6] = XtVaCreateManagedWidget("popupBtn6", xmPushButtonWidgetClass, popupMenu, NULL); popupBtn[7] = XtVaCreateManagedWidget("popupBtn7", xmPushButtonWidgetClass, popupMenu, NULL); /* if this is the first one, then don't allow it to be removed. */ if (appData->staffList == NULL) XtVaSetValues(popupBtn[2], XmNsensitive, False, NULL); XtAddCallback(popupBtn[1], XmNactivateCallback, AddVoiceCB, NULL); XtAddCallback(popupBtn[2], XmNactivateCallback, RemoveVoiceCB, staff); XtAddCallback(popupBtn[3], XmNactivateCallback, ClearVoiceCB, staff); XtAddCallback(popupBtn[4], XmNactivateCallback, PlayVoiceCB, staff); XtAddCallback(popupBtn[5], XmNactivateCallback, PlayAllCB, NULL); XtAddCallback(popupBtn[6], XmNactivateCallback, SaveVoiceCB, staff); XtAddCallback(popupBtn[7], XmNactivateCallback, LoadVoiceCB, staff); } /*--------------------------------------------------------------------* | CreateScore | | Creates the rowcolumn to holds the staffs. Also creates an option | | menu for manipulating the staffs. The staff is inserted into the | | global appData list of scores. | *--------------------------------------------------------------------*/ void CreateScore(Widget parent) { Widget scoreWin; scoreWin = XtVaCreateManagedWidget("scoreWin", xmScrolledWindowWidgetClass, parent, XmNscrollingPolicy, XmAUTOMATIC, NULL); appData->score = XtVaCreateManagedWidget("score", xmRowColumnWidgetClass, scoreWin, XmNadjustLast, FALSE, XmNnumColumns, 1, XmNorientation, XmVERTICAL, XmNpacking, XmPACK_TIGHT, NULL); AddNewStaff(XtDisplay(appData->score), LOCAL_NAME); } /*--------------------------------------------------------------------* | CreateNotebook | *--------------------------------------------------------------------*/ Widget CreateNotebook(Widget parent) { NoteType noteType; Widget notebook, noteButton[LAST_NOTE]; Pixel fg, bg; Display *dsp = XtDisplay(parent); Window win = RootWindowOfScreen(XtScreen(parent)); int d = DefaultDepthOfScreen(XtScreen(parent)); notebook = XtVaCreateManagedWidget("notebook", xmRowColumnWidgetClass, parent, NULL); /* * create a pushbutton for each note type and setup its callback. */ for (noteType = (NoteType)0; noteType < LAST_NOTE; noteType++) { noteButton[noteType] = XtVaCreateManagedWidget(noteName[noteType], xmPushButtonWidgetClass, notebook, XmNlabelType, XmPIXMAP, XmNlabelPixmap, appData->noteTable[noteType].image, NULL); XtAddCallback(noteButton[noteType], XmNactivateCallback, SetNoteCB, (XtPointer)noteType); } return notebook; } /*-------------------------------------------------------------* | Resource Converter: CvtStrToFloat | *-------------------------------------------------------------*/ void CvtStrToFloat (XrmValue *args, Cardinal *nargs, XrmValue *fromVal, XrmValue *toVal) { static float result; if (sscanf((char *)fromVal->addr, "%f", &result) == 1) { toVal->size = sizeof(float); toVal->addr = (XtPointer) &result; } else XtStringConversionWarning((char *)fromVal->addr, "Float"); } /*----------------------------------------------------------------* | GetAppResources | | The following resources are supported in piano: | | .baseDuration: (int) | | -- frequencey in Hz for middle C. | | .baseFrequency: (float) | | -- duration in ms of a quarter note. | | .wkeyCount: (int) | | -- specifies the number of white keys. | | .keyHeight: (int) | | -- white key pixel height. black keys are calculated. | | .keyWidth: (int) | | -- white key pixel width. black keys are calculated. | *----------------------------------------------------------------*/ AppData *GetAppResources (Widget w) { AppData *appData; if ((appData = (AppData *) XtCalloc(1, sizeof(AppData))) == NULL) { printf(EMSG1); exit(0); } XtGetApplicationResources(w, (XtPointer)appData, appRes, XtNumber(appRes), NULL, 0); return (appData); } /*--------------------------------------------------------------------* | GetNoteImagePixmap | *--------------------------------------------------------------------*/ Pixmap GetNoteImagePixmap(Widget w, NoteType note) { Pixel fg, bg; Display *dsp = XtDisplay(w); Window win = RootWindowOfScreen(XtScreen(w)); int d = DefaultDepthOfScreen(XtScreen(w)); unsigned char *data; XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL); switch (note) { case EIGHTH: data = eighth_bits; break; case EIGHTHDOT: data = eighth_dot_bits; break; case EIGHTHSHARP: data = eighth_sharp_bits; break; case EIGHTHDOTSHARP: data = eighth_dot_sharp_bits; break; case QUARTER: data = quarter_bits; break; case QUARTERDOT: data = quarter_dot_bits; break; case QUARTERSHARP: data = quarter_sharp_bits; break; case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break; case HALF: data = half_bits; break; case HALFDOT: data = half_dot_bits; break; case HALFSHARP: data = half_sharp_bits; break; case HALFDOTSHARP: data = half_dot_sharp_bits; break; case REST: data = rest_bits; break; case RESTDOT: data = rest_dot_bits; break; } return(XCreatePixmapFromBitmapData(dsp, win, (char*)data, note_width, note_height, fg, bg, d)); } /*--------------------------------------------------------------------* | GetNoteMaskPixmap | *--------------------------------------------------------------------*/ Pixmap GetNoteMaskPixmap(Widget w, NoteType note) { Pixel fg, bg; Display *dsp = XtDisplay(w); Window win = RootWindowOfScreen(XtScreen(w)); int d = DefaultDepthOfScreen(XtScreen(w)); unsigned char *data; XtVaGetValues(w, XmNforeground, &fg, XmNbackground, &bg, NULL); switch (note) { case EIGHTH: data = eighth_bits; break; case EIGHTHDOT: data = eighth_dot_bits; break; case EIGHTHSHARP: data = eighth_sharp_bits; break; case EIGHTHDOTSHARP: data = eighth_dot_sharp_bits; break; case QUARTER: data = quarter_bits; break; case QUARTERDOT: data = quarter_dot_bits; break; case QUARTERSHARP: data = quarter_sharp_bits; break; case QUARTERDOTSHARP: data = quarter_dot_sharp_bits;break; case HALF: data = half_bits; break; case HALFDOT: data = half_dot_bits; break; case HALFSHARP: data = half_sharp_bits; break; case HALFDOTSHARP: data = half_dot_sharp_bits; break; case REST: data = rest_bits; break; case RESTDOT: data = rest_dot_bits; break; } return (XCreatePixmapFromBitmapData(dsp, win, (char*)data, note_width, note_height, 1, 0, 1)); } /*--------------------------------------------------------------------* | BuildNoteTable | *--------------------------------------------------------------------*/ void BuildNoteTable (Widget w) { NoteType i; for (i = (NoteType)0; inoteTable[i].image = GetNoteImagePixmap(w, i); appData->noteTable[i].mask = GetNoteMaskPixmap(w, i); } } /*--------------------------------------------------------------------* | CreateMenuBar | *--------------------------------------------------------------------*/ void CreateMenuBar (Widget parent) { Cardinal n; Arg args[10]; Widget menuBar; Widget cascade1, cascade2; Widget menuPane1, menuPane2; Widget b1; menuBar = XmCreateMenuBar(parent, "menuBar", NULL, 0); menuPane1 = XmCreatePulldownMenu(menuBar, "menuPane1", NULL, 0); menuPane2 = XmCreatePulldownMenu(menuBar, "menuPane2", NULL, 0); b1 = XtCreateManagedWidget("b1", xmPushButtonWidgetClass, menuPane1, NULL,0); n = 0; XtSetArg(args[n], XmNsubMenuId, menuPane1); n++; cascade1 = XmCreateCascadeButton(menuBar, "cascade1", args, n); XtManageChild(cascade1); n = 0; cascade2 = XmCreateCascadeButton(menuBar, "cascade2", args, n); XtManageChild(cascade2); n = 0; XtSetArg(args[n], XmNmenuHelpWidget, cascade2); n++; XtSetValues(menuBar, args, n); XtAddCallback(b1, XmNactivateCallback, MenuCB, (XtPointer)MENU_QUIT); XtAddCallback(cascade2, XmNactivateCallback, MenuCB, (XtPointer)MENU_HELP); XtManageChild(menuBar); } /*--------------------------------------------------------------------* | Main | *--------------------------------------------------------------------*/ void main(int argc, char **argv) { Widget shell, mainWin, panedWin; Widget keyboard, notebook; int fn; Pixel fg, bg; XGCValues values; shell = XtVaAppInitialize(&context, APP_CLASS, NULL, 0, &argc, argv, fallback, NULL); XSetErrorHandler(MyErrorHandler); XtAddConverter(XtRString, XtRFloat, CvtStrToFloat, NULL, 0); appData = GetAppResources(shell); mainWin = XmCreateMainWindow(shell, "mainWin", NULL, 0); XtManageChild(mainWin); CreateMenuBar(mainWin); panedWin = XtVaCreateManagedWidget("panedWin", xmPanedWindowWidgetClass, mainWin, NULL); keyboard = CreateKeyboard(panedWin); CreateScore(panedWin); BuildNoteTable(panedWin); notebook = CreateNotebook(panedWin); SetAppIcon(shell); XtRealizeWidget(shell); /* get the note GC */ XtVaGetValues(appData->score, XmNforeground, &fg, XmNbackground, &bg, NULL); values.foreground = fg; values.background = bg; appData->noteGC = XtGetGC(appData->score, GCForeground | GCBackground, &values); SetActiveNote(appData->score, QUARTER); /* save the old bell values so that they can be restored. */ GetBell(XtDisplay(shell)); XtAppMainLoop(context); }