$ git clone http://tcclient.ion.nu/tc_client.git
commit 2b70531be90c06305cde01bab209e86403c8f137
Author: Alicia <...>
Date: Wed Sep 14 18:23:00 2016 +0200
tc_client-gtk: break the cameras into two or more rows if it means they can be larger and still fit.
diff --git a/ChangeLog b/ChangeLog
index a87c1af..d28a420 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -19,6 +19,7 @@ tc_client-gtk: when automatically opening cameras is disabled don't open the one
tc_client-gtk: added menu item to hide a camera upon right-click.
tc_client-gtk: limit the camera preview to 640 by 480, scaling down if the input is larger.
tc_client-gtk: when resizing the window, resize the camera pane before the chat pane.
+tc_client-gtk: break the cameras into two or more rows if it means they can be larger and still fit.
tc_client-gtk and camviewer: fixed compatibility with newer libavutil.
tc_client-gtk and camviewer: added compatibility fallbacks for av_image_get_buffer_size() and av_packet_unref()
tc_client-gtk and camviewer: specify a scaling algorithm for libswscale (mandatory for older versions of libswscale)
diff --git a/README b/README
index 8136c3b..d501438 100644
--- a/README
+++ b/README
@@ -21,6 +21,11 @@ Commands supported by tc_client:
/topic <topic> = set the channel topic
/help = list these commands at runtime
+Missing features:
+broadcasting support for channels which require a password to broadcast
+broadcasting support for channels where mods need to approve streams
+support for reviewing and approving streams as a mod
+
Current commands sent by the TC servers that tc_client doesn't know how to handle:
notice (some, notice is used for many functions)
joinsdone
diff --git a/gtkgui.glade b/gtkgui.glade
index b02ee9e..dc2df3f 100644
--- a/gtkgui.glade
+++ b/gtkgui.glade
@@ -1100,6 +1100,7 @@
<object class="GtkBox" id="cambox">
<property name="visible">True</property>
<property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
<child>
<placeholder/>
</child>
diff --git a/utilities/gtk/camviewer.c b/utilities/gtk/camviewer.c
index d9d0713..19a8e0e 100644
--- a/utilities/gtk/camviewer.c
+++ b/utilities/gtk/camviewer.c
@@ -58,7 +58,6 @@
struct viddata
{
- GtkWidget* box;
AVCodec* vdecoder;
AVCodec* vencoder;
AVCodec* adecoder;
@@ -85,43 +84,6 @@ char frombuild=0; // Running from the build directory
PROCESS_INFORMATION coreprocess={.hProcess=0};
#endif
-void updatescaling(struct viddata* data, unsigned int width, unsigned int height)
-{
-// TODO: Move updatescaling into media.c?
- if(!camcount){return;}
- if(!width){width=gtk_widget_get_allocated_width(data->box);}
- if(!height){height=gtk_widget_get_allocated_height(data->box);}
- camsize_scale.width=width/camcount;
- // 3/4 ratio
- camsize_scale.height=camsize_scale.width*3/4;
- unsigned int i;
- unsigned int labelsize=0;
- for(i=0; i<camcount; ++i)
- {
- if(gtk_widget_get_allocated_height(cams[i].label)>labelsize)
- labelsize=gtk_widget_get_allocated_height(cams[i].label);
- }
- // Fit by height
- if(height<camsize_scale.height+labelsize)
- {
- camsize_scale.height=height-labelsize;
- camsize_scale.width=camsize_scale.height*4/3;
- }
- if(camsize_scale.width<8){camsize_scale.width=8;}
- if(camsize_scale.height<1){camsize_scale.height=1;}
- // TODO: wrapping and stuff
- // Rescale current images to fit
- for(i=0; i<camcount; ++i)
- {
- GdkPixbuf* pixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cams[i].cam));
- if(!pixbuf){continue;}
- GdkPixbuf* old=pixbuf;
- pixbuf=gdk_pixbuf_scale_simple(pixbuf, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
- gtk_image_set_from_pixbuf(GTK_IMAGE(cams[i].cam), pixbuf);
- g_object_unref(old);
- }
-}
-
void printchat(const char* text, const char* pm)
{
GtkAdjustment* scroll;
@@ -473,11 +435,8 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
avcodec_open2(cam->actx, data->adecoder, 0);
cam->samples=0;
#endif
- gtk_box_pack_start(GTK_BOX(data->box), cam->box, 0, 0, 0);
+ updatescaling(0, 0, 1);
gtk_widget_show_all(cam->box);
- updatescaling(data, 0, 0);
- while(gtk_events_pending()){gtk_main_iteration();} // Make sure the label gets its size before we calculate scaling
- updatescaling(data, 0, 0);
return 1;
}
if(!strcmp(buf, "Starting outgoing media stream"))
@@ -496,17 +455,13 @@ gboolean handledata(GIOChannel* iochannel, GIOCondition condition, gpointer data
cam->dstframe->data[0]=0;
cam->actx=0;
- gtk_box_pack_start(GTK_BOX(data->box), cam->box, 0, 0, 0);
+ updatescaling(0, 0, 1);
gtk_widget_show_all(cam->box);
- updatescaling(data, 0, 0);
- while(gtk_events_pending()){gtk_main_iteration();} // Make sure the label gets its size before we calculate scaling
- updatescaling(data, 0, 0);
return 1;
}
if(!strncmp(buf, "VideoEnd: ", 10))
{
camera_remove(&buf[10]);
- updatescaling(data, 0, 0);
return 1;
}
if(!strncmp(buf, "Audio: ", 7))
@@ -644,9 +599,9 @@ void togglecam(GtkCheckMenuItem* item, struct viddata* data)
gboolean handleresize(GtkWidget* widget, GdkEventConfigure* event, struct viddata* data)
{
char bottom=autoscroll_before(data->scroll);
- if(event->width!=gtk_widget_get_allocated_width(data->box))
+ if(event->width!=gtk_widget_get_allocated_width(cambox))
{
- updatescaling(data, event->width, 0);
+ updatescaling(event->width, 0, 0);
}
#ifndef _WIN32 // For some reason scrolling as a response to resizing freezes windows
if(bottom){autoscroll_after(data->scroll);}
@@ -657,7 +612,7 @@ gboolean handleresize(GtkWidget* widget, GdkEventConfigure* event, struct viddat
void handleresizepane(GObject* obj, GParamSpec* spec, struct viddata* data)
{
char bottom=autoscroll_before(data->scroll);
- updatescaling(data, 0, gtk_paned_get_position(GTK_PANED(obj)));
+ updatescaling(0, gtk_paned_get_position(GTK_PANED(obj)), 0);
#ifndef _WIN32
if(bottom){autoscroll_after(data->scroll);}
#endif
@@ -1002,7 +957,7 @@ int main(int argc, char** argv)
item=GTK_WIDGET(gtk_builder_get_object(gui, "menuitem_options_settings2"));
g_signal_connect(item, "activate", G_CALLBACK(showsettings), gui);
- data->box=GTK_WIDGET(gtk_builder_get_object(gui, "cambox"));
+ cambox=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")));
diff --git a/utilities/gtk/media.c b/utilities/gtk/media.c
index e589b41..3ed5b65 100644
--- a/utilities/gtk/media.c
+++ b/utilities/gtk/media.c
@@ -50,6 +50,9 @@ unsigned int camcount=0;
#endif
struct size camsize_out={.width=320, .height=240};
struct size camsize_scale={.width=320, .height=240};
+GtkWidget* cambox;
+GtkWidget** camrows=0;
+unsigned int camrowcount=0;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
// Experimental mixer, not sure if it really works
@@ -102,6 +105,7 @@ void camera_remove(const char* id)
break;
}
}
+ updatescaling(0, 0, 1);
}
void camera_removebynick(const char* nick)
@@ -124,6 +128,7 @@ void camera_removebynick(const char* nick)
break;
}
}
+ updatescaling(0, 0, 1);
}
struct camera* camera_find(const char* id)
@@ -292,7 +297,7 @@ GIOChannel* camthread(const char* name, AVCodec* vencoder, unsigned int delay)
int campipe[2];
#ifndef _WIN32
CAM* cam=cam_open(name); // Opening here in case of GUI callbacks
- if(cam){cam_resolution(cam, &camsize_out.width, &camsize_out.height);}
+ if(cam){cam_resolution(cam, (unsigned int*)&camsize_out.width, (unsigned int*)&camsize_out.height);}
pipe(campipe);
camproc=fork();
if(!camproc)
@@ -453,3 +458,79 @@ void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width,
}
}
}
+
+void updatescaling(unsigned int width, unsigned int height, char changedcams)
+{
+ if(!camcount){return;}
+ if(!width){width=gtk_widget_get_allocated_width(GTK_WIDGET(gtk_builder_get_object(gui, "main")));}
+ if(!height){height=gtk_widget_get_allocated_height(GTK_WIDGET(gtk_builder_get_object(gui, "camerascroll")));}
+
+ GtkRequisition label;
+ gtk_widget_get_preferred_size(cams[0].label, &label, 0);
+ camsize_scale.width=1;
+ camsize_scale.height=1;
+ unsigned int rowcount=1;
+ unsigned int rows;
+ for(rows=1; rows<=camcount; ++rows)
+ {
+ struct size scale;
+ unsigned int cams_per_row=camcount/rows;
+ if(camcount%rows){++cams_per_row;}
+ scale.width=width/cams_per_row;
+ // 3/4 ratio
+ scale.height=scale.width*3/4;
+ unsigned int rowheight=height/rows;
+ // Fit by height
+ if(rowheight<scale.height+label.height)
+ {
+ scale.height=rowheight-label.height;
+ scale.width=scale.height*4/3;
+ }
+ if(scale.width>camsize_scale.width) // Check if this number of rows will fit larger cams
+ {
+ camsize_scale.width=scale.width;
+ camsize_scale.height=scale.height;
+ rowcount=rows;
+ }else if(scale.width<camsize_scale.width){break;} // Only getting smaller from here, use the last one that increased
+ }
+
+ unsigned int i;
+ if(rowcount!=camrowcount || changedcams) // Changed the number of rows, shuffle everything around to fit. Or added/removed a camera, in which case we need to shuffle things around anyway
+ {
+ for(i=0; i<camcount; ++i)
+ {
+ g_object_ref(cams[i].box); // Increase reference counts so that they are not deallocated while they are temporarily detached from the rows
+ gtk_container_remove(GTK_CONTAINER(gtk_widget_get_parent(cams[i].box)), cams[i].box);
+ }
+ for(i=0; i<camrowcount; ++i){gtk_widget_destroy(camrows[i]);} // Erase old rows
+ camrowcount=rowcount;
+ camrows=realloc(camrows, sizeof(GtkWidget*)*camrowcount);
+ for(i=0; i<camrowcount; ++i)
+ {
+ camrows[i]=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
+ gtk_box_pack_start(GTK_BOX(cambox), camrows[i], 1, 0, 0);
+ gtk_widget_set_halign(camrows[i], GTK_ALIGN_CENTER);
+ gtk_widget_show(camrows[i]);
+ }
+ unsigned int cams_per_row=camcount/camrowcount;
+ if(camcount%camrowcount){++cams_per_row;}
+ for(i=0; i<camcount; ++i)
+ {
+ gtk_box_pack_start(GTK_BOX(camrows[i/cams_per_row]), cams[i].box, 0, 0, 0);
+ g_object_unref(cams[i].box); // Decrease reference counts once they're attached again
+ }
+ }
+ // libswscale doesn't handle unreasonably small sizes well
+ if(camsize_scale.width<8){camsize_scale.width=8;}
+ if(camsize_scale.height<1){camsize_scale.height=1;}
+ // Rescale current images to fit
+ for(i=0; i<camcount; ++i)
+ {
+ GdkPixbuf* pixbuf=gtk_image_get_pixbuf(GTK_IMAGE(cams[i].cam));
+ if(!pixbuf){continue;}
+ GdkPixbuf* old=pixbuf;
+ pixbuf=gdk_pixbuf_scale_simple(pixbuf, camsize_scale.width, camsize_scale.height, GDK_INTERP_BILINEAR);
+ gtk_image_set_from_pixbuf(GTK_IMAGE(cams[i].cam), pixbuf);
+ g_object_unref(old);
+ }
+}
diff --git a/utilities/gtk/media.h b/utilities/gtk/media.h
index 3a0a701..d2bf77d 100644
--- a/utilities/gtk/media.h
+++ b/utilities/gtk/media.h
@@ -39,8 +39,8 @@ struct camera
};
struct size
{
- unsigned int width;
- unsigned int height;
+ int width;
+ int height;
};
extern struct camera campreview;
extern struct camera* cams;
@@ -53,6 +53,7 @@ extern unsigned int camcount;
#endif
extern struct size camsize_out;
extern struct size camsize_scale;
+extern GtkWidget* cambox;
#if defined(HAVE_AVRESAMPLE) || defined(HAVE_SWRESAMPLE)
extern void camera_playsnd(int audiopipe, struct camera* cam, short* samples, unsigned int samplecount);
@@ -71,3 +72,4 @@ extern gboolean camselect_cancel(GtkWidget* widget, void* x1, void* x2);
extern void camselect_accept(GtkWidget* widget, AVCodec* vencoder);
extern const char* camselect_file(void);
extern void camera_postproc(struct camera* cam, unsigned char* buf, unsigned int width, unsigned int height);
+extern void updatescaling(unsigned int width, unsigned int height, char changedcams);