$ git clone http://tcclient.ion.nu/tc_client.git
commit a905b2c254b562b59cdac56484bda1797129a576
Author: Alicia <...>
Date: Sun May 31 23:36:07 2015 +0200
tc_client-gtk: redesigned the startup window to better support frequenting multiple channels.
diff --git a/ChangeLog b/ChangeLog
index 19ecf4b..4563189 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
0.33:
modbot: fixed finding the duration of videos longer than 59 minutes (conversion from hh:mm:ss format to seconds)
+tc_client-gtk: redesigned the startup window to better support frequenting multiple channels.
0.32:
Added an 'install' make target, adjusted utilities to run tc_client from PATH unless they were run from the build directory (i.e. './<executable>')
Provide feedback on the /ban command.
diff --git a/gtkgui.glade b/gtkgui.glade
index 8a12566..1e20f07 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -2,6 +2,193 @@
<!-- Generated with glade 3.18.3 -->
<interface>
<requires lib="gtk+" version="3.0"/>
+ <object class="GtkWindow" id="channelconfig">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">tc_client</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <signal name="delete-event" handler="gtk_widget_hide_on_delete" swapped="no"/>
+ <child>
+ <object class="GtkBox" id="box6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label6">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Nickname:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="cc_nick">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Channel:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="cc_channel">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label16">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Channel password (if any):</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">5</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="cc_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="input_purpose">password</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">6</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkExpander" id="expander1">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box7">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkLabel" id="label8">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Username:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="acc_username">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label9">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="halign">start</property>
+ <property name="label" translatable="yes">Password:</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="acc_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="input_purpose">password</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <child type="label">
+ <object class="GtkLabel" id="label5">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Account</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">7</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cc_savebutton">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">9</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cc_delete">
+ <property name="label" translatable="yes">Delete</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">10</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
<object class="GtkWindow" id="channelpasswordwindow">
<property name="can_focus">False</property>
<property name="title" translatable="yes">Channel password?</property>
@@ -39,6 +226,72 @@
</object>
</child>
</object>
+ <object class="GtkImage" id="image1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-connect</property>
+ </object>
+ <object class="GtkImage" id="image2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="stock">gtk-add</property>
+ </object>
+ <object class="GtkWindow" id="startwindow">
+ <property name="can_focus">False</property>
+ <property name="title" translatable="yes">tc_client</property>
+ <property name="default_width">200</property>
+ <property name="default_height">200</property>
+ <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
+ <child>
+ <object class="GtkBox" id="startbox">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkMenuBar" id="menubar1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkMenuItem" id="menuitem2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Channel</property>
+ <child type="submenu">
+ <object class="GtkMenu" id="menu2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImageMenuItem" id="start_menu_add">
+ <property name="label" translatable="yes">Add</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="image">image2</property>
+ <property name="use_stock">False</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkImageMenuItem" id="start_menu_connect">
+ <property name="label" translatable="yes">Connect without adding</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="image">image1</property>
+ <property name="use_stock">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
<object class="GtkWindow" id="main">
<property name="can_focus">False</property>
<property name="title" translatable="yes">tc_client</property>
@@ -566,183 +819,4 @@
</object>
</child>
</object>
- <object class="GtkWindow" id="startwindow">
- <property name="can_focus">False</property>
- <property name="title" translatable="yes">tc_client</property>
- <property name="resizable">False</property>
- <signal name="destroy" handler="gtk_main_quit" swapped="no"/>
- <child>
- <object class="GtkBox" id="box6">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkExpander" id="expander1">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box7">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkLabel" id="label8">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Username:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="acc_username">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label9">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Password:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="acc_password">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="input_purpose">password</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="start_rememberacc">
- <property name="label" translatable="yes">Remember account</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">4</property>
- </packing>
- </child>
- </object>
- </child>
- <child type="label">
- <object class="GtkLabel" id="label5">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Account</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label6">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Nickname:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="start_nick">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label7">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="halign">start</property>
- <property name="label" translatable="yes">Channel:</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="start_channel">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="start_rememberchan">
- <property name="label" translatable="yes">Remember nickname and channel</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">5</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="connectbutton">
- <property name="label" translatable="yes">Connect</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">6</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
</interface>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index 9522c9c..c5a7514 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -68,8 +68,8 @@ struct viddata
#endif
GtkTextBuffer* buffer; // TODO: struct buffer array, for PMs
GtkAdjustment* scroll;
- GtkBuilder* gui;
};
+struct viddata* data;
int tc_client[2];
int tc_client_in[2];
@@ -136,9 +136,12 @@ void printchat_color(struct viddata* data, const char* text, const char* color,
int startnum=gtk_text_iter_get_offset(&end);
gtk_text_buffer_insert(data->buffer, &end, text, -1);
// Set color if there was one
- GtkTextIter start;
- gtk_text_buffer_get_iter_at_offset(data->buffer, &start, startnum+offset);
- gtk_text_buffer_apply_tag_by_name(data->buffer, color, &start, &end);
+ if(color)
+ {
+ GtkTextIter start;
+ gtk_text_buffer_get_iter_at_offset(data->buffer, &start, startnum+offset);
+ gtk_text_buffer_apply_tag_by_name(data->buffer, color, &start, &end);
+ }
if(bottom){autoscroll_after(data->scroll);}
}
@@ -146,7 +149,6 @@ char buf[1024];
gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer datap)
{
int fd=g_io_channel_unix_get_fd(iochannel);
- struct viddata* data=datap;
unsigned int i;
for(i=0; i<1023; ++i)
{
@@ -191,8 +193,8 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
}
if(!strcmp(buf, "Password required"))
{
- gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(data->gui, "main")));
- gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(data->gui, "channelpasswordwindow")));
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
+ gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "channelpasswordwindow")));
return 1;
}
if(buf[0]=='/') // For the /help text
@@ -706,16 +708,39 @@ void sendmessage(GtkEntry* entry, struct viddata* data)
gtk_entry_set_text(entry, "");
}
-void startsession(GtkButton* button, struct viddata* data)
+void startsession(GtkButton* button, void* x)
{
- gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(data->gui, "startwindow")));
- gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(data->gui, "channelpasswordwindow")));
- gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(data->gui, "main")));
- const char* nick=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "start_nick")));
- channel=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "start_channel")));
- const char* chanpass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "channelpassword")));
- const char* acc_user=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "acc_username")));
- const char* acc_pass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "acc_password")));
+ if(x!=(void*)-1)
+ {
+ // Populate the quick-connect fields with saved data
+ int channel_id=(int)(intptr_t)x;
+ char buf[256];
+ sprintf(buf, "channel%i_nick", channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_name", channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_password", channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_password")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_accuser", channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_accpass", channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")), config_get_str(buf));
+ // TODO: if account password is empty, open a password entry window similar to the channel password one
+ }
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "startwindow")));
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "channelconfig")));
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "channelpasswordwindow")));
+ gtk_widget_show_all(GTK_WIDGET(gtk_builder_get_object(gui, "main")));
+ const char* nick=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")));
+ channel=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")));
+ const char* chanpass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "channelpassword")));
+ if(!chanpass[0]){chanpass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_password")));}
+ const char* acc_user=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")));
+ const char* acc_pass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")));
pipe(tc_client);
pipe(tc_client_in);
if(!fork())
@@ -737,67 +762,36 @@ void startsession(GtkButton* button, struct viddata* data)
GIOChannel* tcchannel=g_io_channel_unix_new(tc_client[0]);
g_io_channel_set_encoding(tcchannel, 0, 0);
g_io_add_watch(tcchannel, G_IO_IN, handledata, data);
- // Remember, if asked to
- char save=0;
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(data->gui, "start_rememberchan"))))
- {
- config_set("remember_nick", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "start_nick"))));
- config_set("remember_chan", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "start_channel"))));
- config_set("remember_chan_nick", "True");
- save=1;
- }
- else if(config_get_bool("remember_chan_nick")) // Remove previously remembered info
- {
- config_set("remember_nick", "");
- config_set("remember_chan", "");
- config_set("remember_chan_nick", "False");
- save=1;
- }
- // Same for account
- if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(data->gui, "start_rememberacc"))))
- {
- config_set("remember_username", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "acc_username"))));
- config_set("remember_password", gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(data->gui, "acc_password"))));
- config_set("remember_acc", "True");
- save=1;
- }
- else if(config_get_bool("remember_acc")) // Remove previously remembered info
- {
- config_set("remember_username", "");
- config_set("remember_password", "");
- config_set("remember_acc", "False");
- save=1;
- }
- if(save){config_save();}
}
int main(int argc, char** argv)
{
if(!strncmp(argv[0], "./", 2)){frombuild=1;}
- struct viddata data={0,0,0,0,0};
+ struct viddata datax={0,0,0,0,0};
+ data=&datax;
avcodec_register_all();
- data.vdecoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
- data.adecoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER);
+ data->vdecoder=avcodec_find_decoder(AV_CODEC_ID_FLV1);
+ data->adecoder=avcodec_find_decoder(AV_CODEC_ID_NELLYMOSER);
signal(SIGCHLD, SIG_IGN);
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
#ifdef HAVE_AVRESAMPLE
- data.resamplectx=avresample_alloc_context();
- av_opt_set_int(data.resamplectx, "in_channel_layout", AV_CH_FRONT_CENTER, 0);
- av_opt_set_int(data.resamplectx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
+ data->resamplectx=avresample_alloc_context();
+ av_opt_set_int(data->resamplectx, "in_channel_layout", AV_CH_FRONT_CENTER, 0);
+ av_opt_set_int(data->resamplectx, "in_sample_fmt", AV_SAMPLE_FMT_FLT, 0);
// TODO: any way to get the sample rate from the frame/decoder? cam->frame->sample_rate seems to be 0
- av_opt_set_int(data.resamplectx, "in_sample_rate", 11025, 0);
- av_opt_set_int(data.resamplectx, "out_channel_layout", AV_CH_FRONT_CENTER, 0);
- av_opt_set_int(data.resamplectx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
- av_opt_set_int(data.resamplectx, "out_sample_rate", 22050, 0);
- avresample_open(data.resamplectx);
+ av_opt_set_int(data->resamplectx, "in_sample_rate", 11025, 0);
+ av_opt_set_int(data->resamplectx, "out_channel_layout", AV_CH_FRONT_CENTER, 0);
+ av_opt_set_int(data->resamplectx, "out_sample_fmt", AV_SAMPLE_FMT_S16, 0);
+ av_opt_set_int(data->resamplectx, "out_sample_rate", 22050, 0);
+ avresample_open(data->resamplectx);
#else
- data.swrctx=swr_alloc_set_opts(0, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_S16, 22050, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_FLT, 11025, 0, 0);
- swr_init(data.swrctx);
+ data->swrctx=swr_alloc_set_opts(0, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_S16, 22050, AV_CH_FRONT_CENTER, AV_SAMPLE_FMT_FLT, 11025, 0, 0);
+ swr_init(data->swrctx);
#endif
int audiopipe[2];
pipe(audiopipe);
- data.audiopipe=audiopipe[1];
+ data->audiopipe=audiopipe[1];
if(!fork())
{
prctl(PR_SET_PDEATHSIG, SIGHUP);
@@ -809,7 +803,6 @@ int main(int argc, char** argv)
#endif
gtk_init(&argc, &argv);
- GtkBuilder* gui;
if(frombuild)
{
gui=gtk_builder_new_from_file("gtkgui.glade");
@@ -817,12 +810,11 @@ int main(int argc, char** argv)
gui=gtk_builder_new_from_file(PREFIX "/share/tc_client/gtkgui.glade");
}
gtk_builder_connect_signals(gui, 0);
- data.gui=gui;
#ifdef HAVE_V4L2
GtkWidget* item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_broadcast_camera"));
- g_signal_connect(item, "toggled", G_CALLBACK(togglecam), &data);
- data.vencoder=avcodec_find_encoder(AV_CODEC_ID_FLV1);
+ g_signal_connect(item, "toggled", G_CALLBACK(togglecam), data);
+ data->vencoder=avcodec_find_encoder(AV_CODEC_ID_FLV1);
#else
GtkWidget* item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_broadcast"));
gtk_widget_destroy(item);
@@ -831,13 +823,13 @@ int main(int argc, char** argv)
item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_options_settings"));
g_signal_connect(item, "activate", G_CALLBACK(showsettings), gui);
- data.box=GTK_WIDGET(gtk_builder_get_object(gui, "cambox"));
+ data->box=GTK_WIDGET(gtk_builder_get_object(gui, "cambox"));
userlistwidget=GTK_WIDGET(gtk_builder_get_object(gui, "userlistbox"));
GtkWidget* chatview=GTK_WIDGET(gtk_builder_get_object(gui, "chatview"));
- data.scroll=gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gtk_builder_get_object(gui, "chatscroll")));
+ data->scroll=gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(gtk_builder_get_object(gui, "chatscroll")));
- data.buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(chatview));
- #define colormap(code, color) gtk_text_buffer_create_tag(data.buffer, code, "foreground", color, (char*)0)
+ data->buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(chatview));
+ #define colormap(code, color) gtk_text_buffer_create_tag(data->buffer, code, "foreground", color, (char*)0)
colormap("[31", "#821615");
colormap("[31;1", "#c53332");
colormap("[33", "#a08f23");
@@ -856,11 +848,11 @@ int main(int argc, char** argv)
//colormap("[35;1", "#b9807f");
GtkWidget* panes=GTK_WIDGET(gtk_builder_get_object(gui, "vpaned"));
- g_signal_connect(panes, "notify::position", G_CALLBACK(handleresizepane), &data);
+ g_signal_connect(panes, "notify::position", G_CALLBACK(handleresizepane), data);
GtkWidget* inputfield=GTK_WIDGET(gtk_builder_get_object(gui, "inputfield"));
- g_signal_connect(inputfield, "activate", G_CALLBACK(sendmessage), &data);
- g_signal_connect(inputfield, "key-press-event", G_CALLBACK(inputkeys), &data);
+ g_signal_connect(inputfield, "activate", G_CALLBACK(sendmessage), data);
+ g_signal_connect(inputfield, "key-press-event", G_CALLBACK(inputkeys), data);
config_load();
// Sound
@@ -876,29 +868,46 @@ int main(int argc, char** argv)
g_signal_connect(option, "toggled", G_CALLBACK(toggle_youtubecmd), gui);
GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "main"));
- g_signal_connect(window, "configure-event", G_CALLBACK(handleresize), &data);
+ g_signal_connect(window, "configure-event", G_CALLBACK(handleresize), data);
// Start window and channel password window signals
- GtkWidget* button=GTK_WIDGET(gtk_builder_get_object(gui, "connectbutton"));
- g_signal_connect(button, "clicked", G_CALLBACK(startsession), &data);
- button=GTK_WIDGET(gtk_builder_get_object(gui, "channelpasswordbutton"));
- g_signal_connect(button, "clicked", G_CALLBACK(startsession), &data);
+ GtkWidget* button=GTK_WIDGET(gtk_builder_get_object(gui, "channelpasswordbutton"));
+ g_signal_connect(button, "clicked", G_CALLBACK(startsession), (void*)-1); // &data);
button=GTK_WIDGET(gtk_builder_get_object(gui, "channelpassword"));
- g_signal_connect(button, "activate", G_CALLBACK(startsession), &data);
+ g_signal_connect(button, "activate", G_CALLBACK(startsession), (void*)-1); // &data);
GtkWidget* startwindow=GTK_WIDGET(gtk_builder_get_object(gui, "startwindow"));
- // Set channel and nick from last session
- if(config_get_bool("remember_chan_nick"))
- {
- gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "start_nick")), config_get_str("remember_nick"));
- gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "start_channel")), config_get_str("remember_chan"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui, "start_rememberchan")), 1);
- }
- // Set username and password from last session
- if(config_get_bool("remember_acc"))
- {
- gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")), config_get_str("remember_username"));
- gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")), config_get_str("remember_password"));
- gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(gtk_builder_get_object(gui, "start_rememberacc")), 1);
+ // Connect signal for quick connect
+ item=GTK_WIDGET(gtk_builder_get_object(gui, "start_menu_connect"));
+ struct channelopts cc_connect={-1,0};
+ g_signal_connect(item, "activate", G_CALLBACK(channeldialog), &cc_connect);
+ // Connect signal for the add option
+ item=GTK_WIDGET(gtk_builder_get_object(gui, "start_menu_add"));
+ struct channelopts cc_add={-1,1};
+ g_signal_connect(item, "activate", G_CALLBACK(channeldialog), &cc_add);
+ // Populate saved channels
+ GtkWidget* startbox=GTK_WIDGET(gtk_builder_get_object(gui, "startbox"));
+ int channelcount=config_get_int("channelcount");
+ char buf[256];
+ int i;
+ for(i=0; i<channelcount; ++i)
+ {
+ GtkWidget* box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ #ifdef GTK_STYLE_CLASS_LINKED
+ GtkStyleContext* style=gtk_widget_get_style_context(box);
+ gtk_style_context_add_class(style, GTK_STYLE_CLASS_LINKED);
+ #endif
+ sprintf(buf, "channel%i_name", i);
+ const char* name=config_get_str(buf);
+ GtkWidget* connectbutton=gtk_button_new_with_label(name);
+ g_signal_connect(connectbutton, "clicked", G_CALLBACK(startsession), (void*)(intptr_t)i);
+ gtk_box_pack_start(GTK_BOX(box), connectbutton, 1, 1, 0);
+ GtkWidget* cfgbutton=gtk_button_new_from_icon_name("gtk-preferences", GTK_ICON_SIZE_BUTTON);
+ struct channelopts* opts=malloc(sizeof(struct channelopts));
+ opts->channel_id=i;
+ opts->save=1;
+ g_signal_connect(cfgbutton, "clicked", G_CALLBACK(channeldialog), opts);
+ gtk_box_pack_start(GTK_BOX(box), cfgbutton, 0, 0, 0);
+ gtk_box_pack_start(GTK_BOX(startbox), box, 0, 0, 2);
}
gtk_widget_show_all(startwindow);
@@ -906,9 +915,9 @@ int main(int argc, char** argv)
camera_cleanup();
#ifdef HAVE_AVRESAMPLE
- avresample_free(&data.resamplectx);
+ avresample_free(&data->resamplectx);
#elif defined(HAVE_SWRESAMPLE)
- swr_free(&data.swrctx);
+ swr_free(&data->swrctx);
#endif
return 0;
}
diff --git a/utilities/gtk/compat.h b/utilities/gtk/compat.h
index 88004d2..a0a5ee2 100644
--- a/utilities/gtk/compat.h
+++ b/utilities/gtk/compat.h
@@ -22,3 +22,6 @@
extern int gtk_widget_get_allocated_height(GtkWidget* widget);
extern GtkBuilder* gtk_builder_new_from_file(const char* filename);
#endif
+#if GTK_MAJOR_VERSION<3 || GTK_MINOR_VERSION<10
+ #define gtk_button_new_from_icon_name(name, size) gtk_button_new_from_stock(name)
+#endif
diff --git a/utilities/gtk/gui.c b/utilities/gtk/gui.c
index aa7f8df..ded7028 100644
--- a/utilities/gtk/gui.c
+++ b/utilities/gtk/gui.c
@@ -14,10 +14,16 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <stdlib.h>
+#include <string.h>
#include <gtk/gtk.h>
#include "gui.h"
#include "config.h"
#include "logging.h"
+#include "compat.h"
+
+extern void startsession(GtkButton* button, void* x);
+GtkBuilder* gui;
char autoscroll_before(GtkAdjustment* scroll)
{
@@ -109,3 +115,184 @@ void toggle_youtubecmd(GtkToggleButton* button, GtkBuilder* gui)
GtkWidget* field=GTK_WIDGET(gtk_builder_get_object(gui, "youtubecmd"));
gtk_widget_set_sensitive(field, gtk_toggle_button_get_active(button));
}
+
+void savechannel(GtkButton* button, void* x)
+{
+ int channelid;
+ if(x==(void*)-1)
+ {
+ channelid=config_get_int("channelcount");
+ config_set_int("channelcount", channelid+1);
+ }else{
+ channelid=(int)(intptr_t)x;
+ }
+ char buf[256];
+ gtk_widget_hide(GTK_WIDGET(gtk_builder_get_object(gui, "channelconfig")));
+ const char* nick=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")));
+ sprintf(buf, "channel%i_nick", channelid);
+ config_set(buf, nick);
+
+ const char* channel=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")));
+ sprintf(buf, "channel%i_name", channelid);
+ config_set(buf, channel);
+
+ const char* chanpass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_password")));
+ sprintf(buf, "channel%i_password", channelid);
+ config_set(buf, chanpass);
+
+ const char* acc_user=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")));
+ sprintf(buf, "channel%i_accuser", channelid);
+ config_set(buf, acc_user);
+
+ const char* acc_pass=gtk_entry_get_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")));
+ sprintf(buf, "channel%i_accpass", channelid);
+ config_set(buf, acc_pass);
+ config_save();
+
+ if(x==(void*)-1)
+ {
+ GtkWidget* startbox=GTK_WIDGET(gtk_builder_get_object(gui, "startbox"));
+ GtkWidget* box=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ #ifdef GTK_STYLE_CLASS_LINKED
+ GtkStyleContext* style=gtk_widget_get_style_context(box);
+ gtk_style_context_add_class(style, GTK_STYLE_CLASS_LINKED);
+ #endif
+ GtkWidget* connectbutton=gtk_button_new_with_label(channel);
+ g_signal_connect(connectbutton, "clicked", G_CALLBACK(startsession), (void*)(intptr_t)channelid);
+ gtk_box_pack_start(GTK_BOX(box), connectbutton, 1, 1, 0);
+ GtkWidget* cfgbutton=gtk_button_new_from_icon_name("gtk-preferences", GTK_ICON_SIZE_BUTTON);
+ struct channelopts* opts=malloc(sizeof(struct channelopts));
+ opts->channel_id=channelid;
+ opts->save=1;
+ g_signal_connect(cfgbutton, "clicked", G_CALLBACK(channeldialog), opts);
+ gtk_box_pack_start(GTK_BOX(box), cfgbutton, 0, 0, 0);
+ gtk_box_pack_start(GTK_BOX(startbox), box, 0, 0, 2);
+ gtk_widget_show_all(box);
+ }
+}
+
+char deletechannel_found;
+int deletechannel_id;
+void deletechannel_check(GtkWidget* widget, const char* name)
+{
+ if(!GTK_IS_CONTAINER(widget)){return;}
+ GList* list=gtk_container_get_children(GTK_CONTAINER(widget));
+ if(GTK_IS_BUTTON(list->data))
+ {
+ if(!strcmp(gtk_button_get_label(GTK_BUTTON(list->data)), name))
+ {
+ deletechannel_found=1;
+ gtk_widget_destroy(widget);
+ }
+ else if(deletechannel_found)
+ {
+ // After the channel we deleted we have to increment the numbers (data for events) for subsequent buttons, first remove existing events
+ g_signal_handlers_disconnect_matched(list->data, G_SIGNAL_MATCH_FUNC, 0, 0, 0, startsession, 0);
+ g_signal_handlers_disconnect_matched(list->next->data, G_SIGNAL_MATCH_FUNC, 0, 0, 0, channeldialog, 0);
+ // Bind new events
+ g_signal_connect(list->data, "clicked", G_CALLBACK(startsession), (void*)(intptr_t)deletechannel_id);
+ struct channelopts* opts=malloc(sizeof(struct channelopts));
+ opts->channel_id=deletechannel_id;
+ opts->save=1;
+ g_signal_connect(list->next->data, "clicked", G_CALLBACK(channeldialog), opts);
+ ++deletechannel_id;
+ }
+ }
+ g_list_free(list);
+}
+
+void deletechannel(GtkButton* button, void* x)
+{
+ GtkContainer* startbox=GTK_CONTAINER(gtk_builder_get_object(gui, "startbox"));
+ int channelid=(int)(intptr_t)x;
+ char buf[256];
+ sprintf(buf, "channel%i_name", channelid);
+ // Delete the buttons from the startbox
+ deletechannel_found=0;
+ deletechannel_id=channelid;
+ gtk_container_foreach(startbox, (GtkCallback)deletechannel_check, (void*)config_get_str(buf));
+ GtkWidget* window=GTK_WIDGET(gtk_builder_get_object(gui, "channelconfig"));
+ gtk_widget_hide(window);
+ // Delete from the configuration
+ char buf2[256];
+ int channelcount=config_get_int("channelcount")-1;
+ for(;channelid<channelcount; ++channelid)
+ {
+ #define move_channel_var(x) \
+ sprintf(buf, "channel%i_"x, channelid); \
+ sprintf(buf2, "channel%i_"x, channelid+1); \
+ config_set(buf, config_get_str(buf2));
+ move_channel_var("name");
+ move_channel_var("nick");
+ move_channel_var("password");
+ move_channel_var("accuser");
+ move_channel_var("accpass");
+ }
+ // Clear entries from the now unused channel ID
+ sprintf(buf, "channel%i_name", channelcount);
+ config_set(buf, "");
+ sprintf(buf, "channel%i_nick", channelcount);
+ config_set(buf, "");
+ sprintf(buf, "channel%i_password", channelcount);
+ config_set(buf, "");
+ sprintf(buf, "channel%i_accuser", channelcount);
+ config_set(buf, "");
+ sprintf(buf, "channel%i_accpass", channelcount);
+ config_set(buf, "");
+ config_set_int("channelcount", channelcount);
+ config_save();
+}
+
+void channeldialog(GtkButton* button, struct channelopts* opts)
+{
+ // If we're not going from an existing channel, clear the fields
+ if(opts->channel_id==-1)
+ {
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_password")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")), "");
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")), "");
+ }else{ // But if we are, populate the fields
+ char buf[256];
+ sprintf(buf, "channel%i_nick", opts->channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_nick")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_name", opts->channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_channel")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_password", opts->channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "cc_password")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_accuser", opts->channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_username")), config_get_str(buf));
+
+ sprintf(buf, "channel%i_accpass", opts->channel_id);
+ gtk_entry_set_text(GTK_ENTRY(gtk_builder_get_object(gui, "acc_password")), config_get_str(buf));
+ }
+ GObject* obj=gtk_builder_get_object(gui, "cc_savebutton");
+ // Connect the save button either to startsession or savechannel depending on if we're adding/editing or quick-connecting
+ g_signal_handlers_disconnect_matched(obj, G_SIGNAL_MATCH_FUNC, 0, 0, 0, savechannel, 0);
+ g_signal_handlers_disconnect_matched(obj, G_SIGNAL_MATCH_FUNC, 0, 0, 0, startsession, 0);
+ if(opts->save)
+ {
+ gtk_button_set_label(GTK_BUTTON(obj), "Save");
+ g_signal_connect(obj, "clicked", G_CALLBACK(savechannel), (void*)(intptr_t)opts->channel_id);
+ }else{
+ gtk_button_set_label(GTK_BUTTON(obj), "Connect");
+ g_signal_connect(obj, "clicked", G_CALLBACK(startsession), (void*)-1);
+ }
+ obj=gtk_builder_get_object(gui, "channelconfig");
+ gtk_window_set_transient_for(GTK_WINDOW(obj), GTK_WINDOW(gtk_builder_get_object(gui, "startwindow")));
+ gtk_widget_show_all(GTK_WIDGET(obj));
+
+ obj=gtk_builder_get_object(gui, "cc_delete");
+ if(opts->channel_id==-1)
+ {
+ gtk_widget_hide(GTK_WIDGET(obj));
+ }else{
+ // Connect signal for channel delete button
+ g_signal_handlers_disconnect_matched(obj, G_SIGNAL_MATCH_FUNC, 0, 0, 0, deletechannel, 0);
+ g_signal_connect(obj, "clicked", G_CALLBACK(deletechannel), (void*)(intptr_t)opts->channel_id);
+ }
+}
diff --git a/utilities/gtk/gui.h b/utilities/gtk/gui.h
index e4cfa68..971d680 100644
--- a/utilities/gtk/gui.h
+++ b/utilities/gtk/gui.h
@@ -16,6 +16,12 @@
*/
#include <gtk/gtk.h>
+struct channelopts
+{
+ int channel_id; // for editing an existing channel, otherwise pass -1
+ char save;
+};
+
extern char autoscroll_before(GtkAdjustment* scroll);
extern void autoscroll_after(GtkAdjustment* scroll);
extern void settings_reset(GtkBuilder* gui);
@@ -24,3 +30,7 @@ extern void savesettings(GtkButton* button, GtkBuilder* gui);
extern void toggle_soundcmd(GtkToggleButton* button, GtkBuilder* gui);
extern void toggle_logging(GtkToggleButton* button, GtkBuilder* gui);
extern void toggle_youtubecmd(GtkToggleButton* button, GtkBuilder* gui);
+extern void deletechannel(GtkButton* button, void* x);
+extern void channeldialog(GtkButton* button, struct channelopts* opts);
+
+extern GtkBuilder* gui;