Home > Back-end >  How can I make Motif Push Button widget to appear as permanently pushed?
How can I make Motif Push Button widget to appear as permanently pushed?

Time:01-09

I have written a simple tool to select an audio channel in FreeBSD.

This is the code:

xaudio.c

#include <Xm/Xm.h>
#include <Xm/PushB.h>
#include <Xm/MainW.h>
#include <Xm/RowColumn.h>

#include <stdio.h>
#include <stdlib.h>

#include "devices.h"

struct button_data {
   int device_num;
   Widget button;
};

/* GLOBAL VARIABLES */
int num_devices;
struct device *devices;
struct button_data *buttons;

void button_callback(Widget widget, XtPointer client_data, XtPointer call_data);
void create_device_buttons(Widget parent, struct device *devices, int num_devices);

void button_callback(Widget widget, XtPointer client_data, XtPointer call_data) {
   int i;
   struct button_data *data = (struct button_data *)client_data;
   int device_num = data->device_num;

   // unclick all other buttons
   for (i = 0; i < num_devices; i  ) {
      if (buttons[i].button != widget) {
         XtVaSetValues(buttons[i].button, XmNset, False, NULL);
      }
   }

   // click this button
   XtVaSetValues(widget, XmNset, True, NULL);
   
   // set audio device
   set_audio_device(device_num);
}

void create_device_buttons(Widget parent, struct device *devices, int num_devices) {
   int i;
   XmString label;
   struct button_data *data;
   Arg args[5];
   int n;

   buttons = malloc(num_devices * sizeof(struct button_data));
   if (buttons == NULL) {
      perror("Failed to allocate memory");
      exit(EXIT_FAILURE);
   }

   for (i = 0; i < num_devices; i  ) {
      // create button
      n = 0;
      label = XmStringCreateLocalized(devices[i].description);
      XtSetArg(args[n], XmNlabelString, label); n  ;
      buttons[i].button = XmCreatePushButton(parent, "device_button", args, n);
      XmStringFree(label);
      XtManageChild(buttons[i].button);

      // set button data
      buttons[i].device_num = devices[i].number;

      // set button callback
      XtAddCallback(buttons[i].button, XmNactivateCallback, button_callback, &buttons[i]);
   }
}

int main(int argc, char *argv[]) {
   XtAppContext app;
   Widget top_level, main_window, button_rowcolumn;
   int default_device;

   int n=0;
   Arg args[10];

   top_level = XtVaOpenApplication(&app, "XAudio", NULL, 0, &argc, argv, NULL, sessionShellWidgetClass, NULL);
   main_window = XmCreateMainWindow(top_level, "main_window", NULL, 0);
   XtManageChild(main_window);
   n=0;
   XtSetArg(args[n], XmNorientation, XmVERTICAL); n  ;
   button_rowcolumn = XmCreateRowColumn(main_window, "button_rowcolumn", args, n);    
   XtManageChild(button_rowcolumn);

   /* GET LIST OF DEVICES AND DEFAULT DEVICE */
   default_device = get_default_device(&devices, &num_devices);

   int i=0;
   for(i=0; i<num_devices; i  ) {
        printf("%d %s\n", devices[i].number, devices[i].description);
  }

   create_device_buttons(button_rowcolumn, devices, num_devices);
   
   XtRealizeWidget(top_level);
   
   /* CLICK DEFAULT BUTTON */
   if (default_device >= 0) {
      XtVaSetValues(buttons[default_device].button, XmNset, True, NULL);
   }

   XtAppMainLoop(app);
   
   free(devices);
   free(buttons);
   
   return 0;
}

devices.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct device {
   int number;
   char description[256];
};

void set_audio_device(int device_num) {
   char command[256];
   sprintf(command, "sysctl hw.snd.default_unit=%d", device_num);
   system(command);
}

int get_default_device(struct device **devices, int *num_devices) {
   FILE *fp;
   char *line = NULL;
   size_t len = 0;
   ssize_t read;
   int num_dev = 0;
   int default_device = -1;

   fp = popen("cat /dev/sndstat", "r");
   if (fp == NULL) {
      perror("Failed to run command");
      exit(EXIT_FAILURE);
   }

   while ((read = getline(&line, &len, fp)) != -1) {
      if (strstr(line, "pcm") == line) {
         // line starts with "pcm", so it is a device line
         // increase number of devices and reallocate memory
         num_dev  ;
         *devices = realloc(*devices, num_dev * sizeof(struct device));
         if (*devices == NULL) {
            perror("Failed to allocate memory");
            exit(EXIT_FAILURE);
         }

         // get device number
         sscanf(line, "pcm%d:", &(*devices)[num_dev - 1].number);

         // get description
         char *start = strchr(line, '<');
         char *end = strchr(line, '>');
         if (start != NULL && end != NULL) {
            int length = end - start - 1;
            strncpy((*devices)[num_dev - 1].description, start   1, length);
            (*devices)[num_dev - 1].description[length] = '\0';
         }

         // check if this is the default device
         if (strstr(line, "default") != NULL) {
            default_device = num_dev - 1;
         }
      }
   }

   *num_devices = num_dev;
   pclose(fp);
   return default_device;
}

devices.h:

#ifndef DEVICES_H
#define DEVICES_H

#include <stdio.h>
#include <stdlib.h>

struct device {
   int number;
   char description[256];
};

void set_audio_device(int device_num);
int get_default_device(struct device **devices, int *num_devices);

#endif

And the corresponding makefile (for FreeBSD):

CC = cc
CFLAGS = -Wall -Wextra -I/usr/local/include
LDFLAGS = -Wall -Wextra -L/usr/local/lib -lXm -lXt -lX11 

all: xaudio

xaudio: xaudio.o devices.o
        $(CC) $(LDFLAGS) -o $@ xaudio.o devices.o

xaudio.o: xaudio.c devices.h
        $(CC) $(CFLAGS) -c -o $@ $<

devices.o: devices.c devices.h
        $(CC) $(CFLAGS) -c -o $@ $<

clean:
        rm -f *.o xaudio

The program compiles and works (it switches the audio channel as expected). However, my intended behaviour was that the channel selected appeared as a permanently pushed button, and that when a new channel was selected the pushed button changed accordingly. Sort kind of "selectable button set".

It is my understanding that such behaviour was achieved by the following widget parameter:

XtVaSetValues(buttons[default_device].button, XmNset, True, NULL);

However, as seen here, this is not happening and the buttons appear always unclicked except the normal dynamic click effect when pressed.

enter image description here

CodePudding user response:

The desired behaviour you describe is what "radio buttons" are for. In Motif, I believe you need the XmCreateRadioBox function.

CodePudding user response:

No, XmPushButton cant be configured to looked always pushed. Try using XmToggleButton instead of XmPushButton. Set XmNindicatorOn to False to get rid of the diamond checkbox. Then XmNset,False will make it looked pressed.

  • Related