mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-22 04:15:54 -04:00
resync with recent trunk changes, contenttype merge and final bits of
handling shoutcast DSP client. svn path=/icecast/branches/kh/icecast/; revision=8233
This commit is contained in:
parent
4b94e72ca3
commit
7afbc8a5f8
2
AUTHORS
2
AUTHORS
@ -1,4 +1,4 @@
|
||||
Jack Moffitt <jack@icecast.org>
|
||||
Michael Smith <msmith@icecast.org>
|
||||
oddsock <oddsock@oddsock.org>
|
||||
oddsock <oddsock@xiph.org>
|
||||
Karl Heyes <karl@xiph.org>
|
||||
|
45
README
45
README
@ -1,8 +1,41 @@
|
||||
Icecast2 Beta 1.
|
||||
icecast 2.x - README
|
||||
---------------------------------------------------------------------
|
||||
|
||||
This is an beta release. Not all functionality is fully implemented, though
|
||||
we believe it to be very stable for all the currently available features.
|
||||
Icecast is a streaming media server which currently supports Ogg
|
||||
Vorbis and MP3 audio streams. It can be used to create an Internet
|
||||
radio station or a privately running jukebox and many things in
|
||||
between. It is very versatile in that new formats can be added
|
||||
relatively easily and supports open standards for commuincation and
|
||||
interaction.
|
||||
|
||||
Icecast is distributed under the GNU GPL, version 2. A copy of this
|
||||
license is included with this software in the COPYING file.
|
||||
|
||||
Prerequisites
|
||||
---------------------------------------------------------------------
|
||||
icecast requires the following packages :
|
||||
|
||||
* libxml2 - http://xmlsoft.org/downloads.html
|
||||
* libxslt - http://xmlsoft.org/XSLT/downloads.html
|
||||
* curl - http://curl.haxx.se/download.html (>= version 7.10 required)
|
||||
NOTE: icecast may be compiled without curl, however this will
|
||||
disable all Directory server interaction (YP).
|
||||
* ogg/vorbis - http://www.vorbis.com/files (>= version 1.0 required)
|
||||
|
||||
A Note About RPMS
|
||||
---------------------------------------------------------------------
|
||||
This section only applies to you if your operating system uses RPMS.
|
||||
|
||||
In order to build icecast, you will need to install the "devel" RPM
|
||||
packages for each of the prerequisite packages in addition to the
|
||||
normal RPMS for each package.
|
||||
|
||||
please check the websites for each of the prerequisite packages for
|
||||
appropriate download links for RPMS.
|
||||
|
||||
|
||||
Build/Install
|
||||
---------------------------------------------------------------------
|
||||
To build icecast on a Unix platform, perform the following :
|
||||
|
||||
Run
|
||||
@ -12,9 +45,11 @@ Run
|
||||
|
||||
To build and install this release.
|
||||
|
||||
A sample config file will be placed in /usr/local/etc (on UNIX) or in the current working directory (on Win32) and is called icecast.xml
|
||||
A sample config file will be placed in /usr/local/etc (on UNIX) or in
|
||||
the current working directory (on Win32) and is called icecast.xml
|
||||
|
||||
Documentation for icecast is available in the doc directory, by viewing doc/icecast2_TOC.html in a browser.
|
||||
Documentation for icecast is available in the doc directory, by
|
||||
viewing doc/index.html in a browser.
|
||||
|
||||
Please email us at icecast@xiph.org or icecast-dev@xiph.org, or come and see
|
||||
us at irc.freenode.net, channel #icecast, if you have any troubles.
|
||||
|
46
TODO
46
TODO
@ -1,33 +1,13 @@
|
||||
2.0 CRITICAL - These are the things without which 2.0 cannot be released
|
||||
____________
|
||||
|
||||
- Should icecast automatically (i.e. without needing -c) look for the config
|
||||
file in /etc/icecast.xml or something?
|
||||
|
||||
- libshout 2.0 and ices 2.0 releases, also an ices 0.x release that works with
|
||||
this. Without source clients, icecast isn't much use...
|
||||
|
||||
- integrate/include all the documentation done by external groups.
|
||||
|
||||
- generally we don't do proper checking for the correct versions of various
|
||||
libraries (this is probably more of an issue with ices2, but it also affects
|
||||
icecast)
|
||||
|
||||
BUGS
|
||||
----
|
||||
- stats get off? this needs testing more testing.
|
||||
|
||||
- some stuff (like 'genre') isn't making it into the stats dump
|
||||
|
||||
- logging - bytes send and time listening may both be broken?
|
||||
|
||||
- slave servers don't work. relay user is not respected by the source (only
|
||||
admin can read /admin/streamlist), and the slave can't parse the xml result
|
||||
of streamlist anyway (it expects a simple mountpoint per line)
|
||||
|
||||
FEATURES
|
||||
--------
|
||||
|
||||
- Should icecast automatically (i.e. without needing -c) look for the config
|
||||
file in /etc/icecast.xml or something?
|
||||
|
||||
- pull out vorbis comments. and send to stats. This seems to be being
|
||||
done, but it isn't working right.
|
||||
|
||||
@ -45,41 +25,21 @@ FEATURES
|
||||
- allow using get_predata() stuff to send an "intro" to any newly-connected
|
||||
user?
|
||||
|
||||
- stats to list currently connected clients: ip and hostname
|
||||
|
||||
- stream switching (drop clients to another stream on disconnect of source)
|
||||
- a) fallbacks from named location to new mountpoint
|
||||
- OR b) fallbacks for connected clients to new mountpoint (so newly-connecting
|
||||
clients just get a 404 on the old path)
|
||||
- OR c) combination - first one, plus generic alias ability?
|
||||
|
||||
- /admin/* for all admin functionality
|
||||
- configuring fallbacks
|
||||
- mp3 metadata injection
|
||||
- remote shutdown?
|
||||
|
||||
- general registerable url-handlers in connection.c rather than hard-coded list
|
||||
(already getting unmaintainable)
|
||||
|
||||
- httpp - split out query string for further processing
|
||||
|
||||
- option to use ipv6 (equiv to using <bind-address>::</bindaddress>, I think.
|
||||
|
||||
- abstract all admin functionality to a set of commands, and command handlers.
|
||||
Make /admin/* just parse according to a set of rules, and dispatch generic
|
||||
commands through that.
|
||||
Use this for alternative admin interfaces (GUI? telnet interface?)
|
||||
|
||||
- listener authentication (per mountpoint?)
|
||||
|
||||
- all timer-based functionality (yp updates, slave/relay checks) should have a
|
||||
single timer thread which dispatches events through the normal event
|
||||
mechanism (to worker threads from the main pool). This will reduce the
|
||||
extraneous thread count.
|
||||
|
||||
- atomic admin function to: set fallback from A->B, remove A, move mountpoint
|
||||
B to A. Needs forced-source removal first.
|
||||
|
||||
- race condition between avl_tree_unlock(pending_tree) and
|
||||
thread_cond_wait(&fserv_cond) in fserv.c, it's a pain to fix but should be.
|
||||
|
||||
|
@ -138,6 +138,12 @@
|
||||
|
||||
<fileserve>1</fileserve>
|
||||
|
||||
<!-- set the mountpoint for a shoutcast source to use, the default if not
|
||||
specified is /stream but you can change it here if an alternative is
|
||||
wanted or an extension is required
|
||||
<shoutcast-mount>/live.nsv</shoutcast-mount>
|
||||
-->
|
||||
|
||||
<paths>
|
||||
<!-- basedir is only used if chroot is enabled -->
|
||||
<basedir>@pkgdatadir@</basedir>
|
||||
|
@ -74,7 +74,7 @@ XIPH_PATH_XSLT
|
||||
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$XSLT_CFLAGS])
|
||||
XIPH_VAR_PREPEND([XIPH_LIBS],[$XSLT_LIBS])
|
||||
|
||||
XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 installed!]))
|
||||
XIPH_PATH_VORBIS(, AC_MSG_ERROR([must have Ogg Vorbis v1.0 or above installed!]))
|
||||
XIPH_VAR_APPEND([XIPH_CPPFLAGS],[$VORBIS_CFLAGS])
|
||||
XIPH_VAR_PREPEND([XIPH_LIBS],[$VORBIS_LIBS])
|
||||
XIPH_VAR_APPEND([XIPH_LDFLAGS],[$VORBIS_LDFLAGS])
|
||||
|
BIN
doc/icecast2.chm
BIN
doc/icecast2.chm
Binary file not shown.
@ -1,5 +1,5 @@
|
||||
Name: icecast
|
||||
Version: 2.0.0
|
||||
Version: 2.1.0-kh
|
||||
Release: 1
|
||||
Summary: Xiph Streaming media server that supports multiple audio formats.
|
||||
|
||||
|
91
src/admin.c
91
src/admin.c
@ -148,14 +148,14 @@ int admin_get_command(char *command)
|
||||
return COMMAND_TRANSFORMED_KILL_CLIENT;
|
||||
else if(!strcmp(command, KILLSOURCE_RAW_REQUEST))
|
||||
return COMMAND_RAW_KILL_SOURCE;
|
||||
else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
|
||||
return COMMAND_RAW_MANAGEAUTH;
|
||||
else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
|
||||
return COMMAND_BUILDM3U;
|
||||
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
|
||||
return COMMAND_TRANSFORMED_MANAGEAUTH;
|
||||
else if(!strcmp(command, KILLSOURCE_TRANSFORMED_REQUEST))
|
||||
return COMMAND_TRANSFORMED_KILL_SOURCE;
|
||||
else if(!strcmp(command, MANAGEAUTH_RAW_REQUEST))
|
||||
return COMMAND_RAW_MANAGEAUTH;
|
||||
else if(!strcmp(command, MANAGEAUTH_TRANSFORMED_REQUEST))
|
||||
return COMMAND_TRANSFORMED_MANAGEAUTH;
|
||||
else if(!strcmp(command, BUILDM3U_RAW_REQUEST))
|
||||
return COMMAND_BUILDM3U;
|
||||
else if(!strcmp(command, ADM_FUNCTION_RAW_REQUEST))
|
||||
return COMMAND_RAW_ADMIN_FUNCTION;
|
||||
else if(!strcmp(command, ADM_FUNCTION_TRANSFORMED_REQUEST))
|
||||
@ -236,8 +236,8 @@ xmlDocPtr admin_build_sourcelist(char *current_source)
|
||||
snprintf (buf, sizeof(buf), "%lu",
|
||||
(unsigned long)(now - source->con->con_time));
|
||||
xmlNewChild (srcnode, NULL, "Connected", buf);
|
||||
xmlNewChild (srcnode, NULL, "Format",
|
||||
source->format->format_description);
|
||||
xmlNewChild (srcnode, NULL, "content-type",
|
||||
source->format->contenttype);
|
||||
if (source->authenticator)
|
||||
{
|
||||
xmlNewChild(srcnode, NULL, "authenticator",
|
||||
@ -321,9 +321,18 @@ void admin_handle_request(client_t *client, char *uri)
|
||||
}
|
||||
|
||||
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE) {
|
||||
ice_config_t *config = config_get_config ();
|
||||
|
||||
ice_config_t *config;
|
||||
char *pass = httpp_get_query_param (client->parser, "pass");
|
||||
if (pass == NULL)
|
||||
{
|
||||
client_send_400 (client, "missing pass parameter");
|
||||
return;
|
||||
}
|
||||
config = config_get_config ();
|
||||
httpp_set_query_param (client->parser, "mount", config->shoutcast_mount);
|
||||
httpp_setvar (client->parser, HTTPP_VAR_PROTOCOL, "ICY");
|
||||
httpp_setvar (client->parser, HTTPP_VAR_ICYPASSWORD, pass);
|
||||
config_release_config ();
|
||||
}
|
||||
|
||||
@ -336,9 +345,18 @@ void admin_handle_request(client_t *client, char *uri)
|
||||
client->authenticated = 1;
|
||||
}
|
||||
/* This is a mount request, but admin user is allowed */
|
||||
if (client->authenticated != 1) {
|
||||
if (connection_check_admin_pass(client->parser))
|
||||
client->authenticated = 1;
|
||||
if (client->authenticated != 1)
|
||||
{
|
||||
if (connection_check_admin_pass(client->parser) == 0)
|
||||
{
|
||||
if (connection_check_source_pass(client->parser, mount) == 0)
|
||||
{
|
||||
INFO1("Bad or missing password on mount modification admin "
|
||||
"request (command: %s)", command_string);
|
||||
client_send_401(client);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avl_tree_rlock(global.source_tree);
|
||||
@ -352,31 +370,26 @@ void admin_handle_request(client_t *client, char *uri)
|
||||
client_send_400 (client, "Source does not exist");
|
||||
}
|
||||
else
|
||||
{
|
||||
INFO2("Received admin command %s on mount \"%s\"",
|
||||
command_string, mount);
|
||||
if (source->shoutcast_compat == 0)
|
||||
{
|
||||
if (source->running == 0 && source->on_demand == 0)
|
||||
{
|
||||
avl_tree_unlock (global.source_tree);
|
||||
INFO2("Received admin command %s on unavailable mount \"%s\"",
|
||||
command_string, mount);
|
||||
avl_tree_unlock (global.source_tree);
|
||||
client_send_400 (client, "Source is not available");
|
||||
return;
|
||||
}
|
||||
if (client->authenticated != 1)
|
||||
if (command == COMMAND_SHOUTCAST_METADATA_UPDATE &&
|
||||
source->shoutcast_compat == 0)
|
||||
{
|
||||
if (connection_check_source_pass(client->parser, mount) == 0)
|
||||
{
|
||||
INFO1("Bad or missing password on mount modification admin "
|
||||
"request (command: %s)", command_string);
|
||||
avl_tree_unlock (global.source_tree);
|
||||
client_send_401(client);
|
||||
ERROR0 ("illegal change of metadata on non-shoutcast "
|
||||
"compatible stream");
|
||||
client_send_400 (client, "illegal metadata call");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
INFO2("Received admin command %s on mount \"%s\"",
|
||||
command_string, mount);
|
||||
admin_handle_mount_request (client, source, command);
|
||||
avl_tree_unlock(global.source_tree);
|
||||
}
|
||||
@ -940,27 +953,11 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
|
||||
{
|
||||
char *action;
|
||||
char *value;
|
||||
char *source_pass;
|
||||
char *config_source_pass;
|
||||
ice_config_t *config;
|
||||
|
||||
DEBUG0("Got shoutcast metadata update request");
|
||||
|
||||
COMMAND_REQUIRE(client, "mode", action);
|
||||
COMMAND_REQUIRE(client, "song", value);
|
||||
COMMAND_REQUIRE(client, "pass", source_pass);
|
||||
|
||||
config = config_get_config();
|
||||
config_source_pass = strdup(config->source_password);
|
||||
config_release_config();
|
||||
|
||||
if ((source->format->type != FORMAT_TYPE_MP3) &&
|
||||
(source->format->type != FORMAT_TYPE_NSV))
|
||||
{
|
||||
thread_mutex_unlock (&source->lock);
|
||||
client_send_400 (client, "Not mp3 or NSV, cannot update metadata");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp (action, "updinfo") != 0)
|
||||
{
|
||||
@ -969,18 +966,6 @@ static void command_shoutcast_metadata(client_t *client, source_t *source)
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(source_pass, config_source_pass) != 0)
|
||||
{
|
||||
thread_mutex_unlock (&source->lock);
|
||||
ERROR0("Invalid source password specified, metadata not updated");
|
||||
client_send_400 (client, "Invalid source password");
|
||||
return;
|
||||
}
|
||||
|
||||
if (config_source_pass) {
|
||||
free(config_source_pass);
|
||||
}
|
||||
|
||||
if (source->format && source->format->set_tag)
|
||||
{
|
||||
source->format->set_tag (source->format, "title", value);
|
||||
|
@ -464,7 +464,7 @@ int connection_complete_source (source_t *source)
|
||||
{
|
||||
WARN0("No content-type header, falling back to backwards compatibility mode "
|
||||
"for icecast 1.x relays. Assuming content is mp3.");
|
||||
format_type = FORMAT_TYPE_MP3;
|
||||
format_type = FORMAT_TYPE_GENERIC;
|
||||
}
|
||||
|
||||
if (format_get_plugin (format_type, source) < 0)
|
||||
@ -582,12 +582,17 @@ int connection_check_admin_pass(http_parser_t *parser)
|
||||
ice_config_t *config = config_get_config();
|
||||
char *pass = config->admin_password;
|
||||
char *user = config->admin_username;
|
||||
char *protocol;
|
||||
|
||||
if(!pass || !user) {
|
||||
config_release_config();
|
||||
return 0;
|
||||
}
|
||||
|
||||
protocol = httpp_getvar (parser, HTTPP_VAR_PROTOCOL);
|
||||
if (protocol && strcmp (protocol, "ICY") == 0)
|
||||
ret = _check_pass_icy (parser, pass);
|
||||
else
|
||||
ret = _check_pass_http (parser, user, pass);
|
||||
config_release_config();
|
||||
return ret;
|
||||
@ -881,9 +886,8 @@ static void _handle_get_request(connection_t *con,
|
||||
if (uri != passed_uri) free (uri);
|
||||
}
|
||||
|
||||
void _handle_shoutcast_compatible(connection_t *con, char *source_password) {
|
||||
void _handle_shoutcast_compatible(connection_t *con, char *mount, char *source_password) {
|
||||
char shoutcast_password[256];
|
||||
char shoutcast_source[256];
|
||||
char *http_compliant;
|
||||
int http_compliant_len = 0;
|
||||
char header[4096];
|
||||
@ -924,18 +928,14 @@ void _handle_shoutcast_compatible(connection_t *con, char *source_password) {
|
||||
/* Here we create a valid HTTP request based of the information
|
||||
that was passed in via the non-HTTP style protocol above. This
|
||||
means we can use some of our existing code to handle this case */
|
||||
memset(shoutcast_source, 0, sizeof (shoutcast_source));
|
||||
strcpy(shoutcast_source, "SOURCE / HTTP/1.0\r\n");
|
||||
http_compliant_len = strlen(shoutcast_source) +
|
||||
strlen(header) + 1;
|
||||
http_compliant_len = strlen(header) + strlen(mount) + 20;
|
||||
http_compliant = (char *)calloc(1, http_compliant_len);
|
||||
sprintf(http_compliant, "%s%s", shoutcast_source,
|
||||
header);
|
||||
snprintf (http_compliant, http_compliant_len,
|
||||
"SOURCE %s HTTP/1.0\r\n%s", mount, header);
|
||||
parser = httpp_create_parser();
|
||||
httpp_initialize(parser, NULL);
|
||||
if (httpp_parse(parser, http_compliant,
|
||||
strlen(http_compliant))) {
|
||||
_handle_source_request(con, parser, "/", SHOUTCAST_SOURCE_AUTH);
|
||||
if (httpp_parse(parser, http_compliant, strlen(http_compliant))) {
|
||||
_handle_source_request(con, parser, mount, SHOUTCAST_SOURCE_AUTH);
|
||||
free(http_compliant);
|
||||
return;
|
||||
}
|
||||
@ -990,10 +990,12 @@ static void *_handle_connection(void *arg)
|
||||
if(global.serversock[i] == con->serversock) {
|
||||
config = config_get_config();
|
||||
if (config->listeners[i].shoutcast_compat) {
|
||||
char *shoutcast_mount = strdup (config->shoutcast_mount);
|
||||
source_password = strdup(config->source_password);
|
||||
config_release_config();
|
||||
_handle_shoutcast_compatible(con, source_password);
|
||||
_handle_shoutcast_compatible(con, shoutcast_mount, source_password);
|
||||
free(source_password);
|
||||
free (shoutcast_mount);
|
||||
continue_flag = 1;
|
||||
break;
|
||||
}
|
||||
|
62
src/format.c
62
src/format.c
@ -58,75 +58,29 @@ format_type_t format_get_type(char *contenttype)
|
||||
return FORMAT_TYPE_OGG; /* Backwards compatibility */
|
||||
else if(strcmp(contenttype, "application/ogg") == 0)
|
||||
return FORMAT_TYPE_OGG; /* Now blessed by IANA */
|
||||
else if(strcmp(contenttype, "audio/mpeg") == 0)
|
||||
return FORMAT_TYPE_MP3;
|
||||
else if(strcmp(contenttype, "audio/x-mpeg") == 0)
|
||||
return FORMAT_TYPE_MP3;
|
||||
else if(strcmp(contenttype, "video/nsv") == 0)
|
||||
return FORMAT_TYPE_NSV;
|
||||
else if(strcmp(contenttype, "audio/aac") == 0)
|
||||
return FORMAT_TYPE_AAC;
|
||||
else if(strcmp(contenttype, "audio/aacp") == 0)
|
||||
return FORMAT_TYPE_AACPLUS;
|
||||
else
|
||||
return FORMAT_ERROR;
|
||||
}
|
||||
|
||||
const char *format_get_mimetype(format_type_t type)
|
||||
{
|
||||
switch(type) {
|
||||
case FORMAT_TYPE_OGG:
|
||||
return "application/ogg";
|
||||
break;
|
||||
case FORMAT_TYPE_MP3:
|
||||
return "audio/mpeg";
|
||||
break;
|
||||
case FORMAT_TYPE_NSV:
|
||||
return "video/nsv";
|
||||
break;
|
||||
case FORMAT_TYPE_AAC:
|
||||
return "audio/aac";
|
||||
break;
|
||||
case FORMAT_TYPE_AACPLUS:
|
||||
return "audio/aacp";
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
/* We default to the Generic format handler, which
|
||||
can handle many more formats than just mp3 */
|
||||
return FORMAT_TYPE_GENERIC;
|
||||
}
|
||||
|
||||
int format_get_plugin(format_type_t type, source_t *source)
|
||||
{
|
||||
int ret = -1;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
switch (type) {
|
||||
case FORMAT_TYPE_OGG:
|
||||
ret = format_ogg_get_plugin (source);
|
||||
break;
|
||||
case FORMAT_TYPE_MP3:
|
||||
case FORMAT_TYPE_GENERIC:
|
||||
ret = format_mp3_get_plugin (source);
|
||||
break;
|
||||
case FORMAT_TYPE_NSV:
|
||||
ret = format_mp3_get_plugin (source);
|
||||
source->format->format_description = "NSV Video";
|
||||
source->format->type = FORMAT_TYPE_NSV;
|
||||
break;
|
||||
case FORMAT_TYPE_AAC:
|
||||
ret = format_mp3_get_plugin (source);
|
||||
source->format->format_description = "AAC Audio";
|
||||
source->format->type = FORMAT_TYPE_AAC;
|
||||
break;
|
||||
case FORMAT_TYPE_AACPLUS:
|
||||
ret = format_mp3_get_plugin (source);
|
||||
source->format->format_description = "AACPlus Audio";
|
||||
source->format->type = FORMAT_TYPE_AACPLUS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (ret < 0)
|
||||
stats_event (source->mount, "content-type",
|
||||
format_get_mimetype(source->format->type));
|
||||
source->format->contenttype);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -274,7 +228,7 @@ static int format_prepare_headers (source_t *source, client_t *client)
|
||||
client->respcode = 200;
|
||||
|
||||
bytes = snprintf (ptr, remaining, "HTTP/1.0 200 OK\r\n"
|
||||
"Content-Type: %s\r\n", format_get_mimetype (source->format->type));
|
||||
"Content-Type: %s\r\n", source->format->contenttype);
|
||||
|
||||
remaining -= bytes;
|
||||
ptr += bytes;
|
||||
|
@ -29,11 +29,7 @@ typedef enum _format_type_tag
|
||||
{
|
||||
FORMAT_ERROR, /* No format, source not processable */
|
||||
FORMAT_TYPE_OGG,
|
||||
FORMAT_TYPE_VORBIS,
|
||||
FORMAT_TYPE_MP3,
|
||||
FORMAT_TYPE_NSV,
|
||||
FORMAT_TYPE_AAC,
|
||||
FORMAT_TYPE_AACPLUS
|
||||
FORMAT_TYPE_GENERIC
|
||||
} format_type_t;
|
||||
|
||||
typedef struct _format_plugin_tag
|
||||
@ -43,7 +39,7 @@ typedef struct _format_plugin_tag
|
||||
/* we need to know the mount to report statistics */
|
||||
char *mount;
|
||||
|
||||
char *format_description;
|
||||
char *contenttype;
|
||||
|
||||
refbuf_t *(*get_buffer)(struct source_tag *);
|
||||
int (*write_buf_to_client)(struct source_tag *source, client_t *client);
|
||||
@ -59,7 +55,6 @@ typedef struct _format_plugin_tag
|
||||
} format_plugin_t;
|
||||
|
||||
format_type_t format_get_type(char *contenttype);
|
||||
const char *format_get_mimetype(format_type_t type);
|
||||
int format_get_plugin(format_type_t type, struct source_tag *source);
|
||||
|
||||
int format_generic_write_to_client (struct source_tag *source, client_t *client);
|
||||
|
@ -82,7 +82,7 @@ int format_mp3_get_plugin (source_t *source)
|
||||
|
||||
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
|
||||
|
||||
plugin->type = FORMAT_TYPE_MP3;
|
||||
plugin->type = FORMAT_TYPE_GENERIC;
|
||||
plugin->get_buffer = mp3_get_no_meta;
|
||||
plugin->write_buf_to_client = format_mp3_write_buf_to_client;
|
||||
plugin->write_buf_to_file = write_mp3_to_file;
|
||||
@ -90,9 +90,14 @@ int format_mp3_get_plugin (source_t *source)
|
||||
plugin->free_plugin = format_mp3_free_plugin;
|
||||
plugin->set_tag = mp3_set_tag;
|
||||
plugin->prerelease = NULL;
|
||||
plugin->format_description = "MP3 audio";
|
||||
plugin->apply_settings = format_mp3_apply_settings;
|
||||
|
||||
plugin->contenttype = httpp_getvar (source->parser, "content-type");
|
||||
if (plugin->contenttype == NULL) {
|
||||
/* We default to MP3 audio for old clients without content types */
|
||||
plugin->contenttype = "audio/mpeg";
|
||||
}
|
||||
|
||||
plugin->_state = state;
|
||||
|
||||
/* initial metadata needs to be blank for sending to clients and for
|
||||
|
@ -551,7 +551,6 @@ int format_ogg_get_plugin (source_t *source)
|
||||
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
|
||||
|
||||
plugin->type = FORMAT_TYPE_OGG;
|
||||
plugin->format_description = "Ogg Vorbis";
|
||||
plugin->get_buffer = ogg_get_buffer;
|
||||
plugin->write_buf_to_client = write_buf_to_client;
|
||||
plugin->write_buf_to_file = write_ogg_to_file;
|
||||
@ -559,6 +558,7 @@ int format_ogg_get_plugin (source_t *source)
|
||||
plugin->free_plugin = format_ogg_free_plugin;
|
||||
plugin->set_tag = NULL;
|
||||
plugin->prerelease = refbuf_page_prerelease;
|
||||
plugin->contenttype = "application/ogg";
|
||||
|
||||
ogg_sync_init (&state->oy);
|
||||
|
||||
|
@ -121,13 +121,13 @@ int format_ogg_get_plugin (source_t *source)
|
||||
plugin = (format_plugin_t *)calloc(1, sizeof(format_plugin_t));
|
||||
|
||||
plugin->type = FORMAT_TYPE_OGG;
|
||||
plugin->format_description = "Ogg Vorbis";
|
||||
plugin->get_buffer = vorbis_get_buffer;
|
||||
plugin->write_buf_to_client = vorbis_write_buf_to_client;
|
||||
plugin->write_buf_to_file = write_vorbis_to_file;
|
||||
plugin->create_client_data = create_vorbis_client_data;
|
||||
plugin->free_plugin = format_vorbis_free_plugin;
|
||||
plugin->set_tag = vorbis_set_tag;
|
||||
plugin->contenttype = "application/ogg";
|
||||
|
||||
state = (vstate_t *)calloc(1, sizeof(vstate_t));
|
||||
ogg_sync_init(&state->oy);
|
||||
|
@ -427,14 +427,14 @@ int fserve_client_create(client_t *httpclient, char *path)
|
||||
bytes = sock_write(httpclient->con->sock,
|
||||
"HTTP/1.1 206 Partial Content\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Content-Length: %ld\r\n"
|
||||
"Content-Length: " FORMAT_INT64 "\r\n"
|
||||
"Content-Range: bytes " FORMAT_INT64 \
|
||||
"-" FORMAT_INT64 "/" FORMAT_INT64 "\r\n"
|
||||
"Content-Type: %s\r\n\r\n",
|
||||
currenttime,
|
||||
new_content_len,
|
||||
rangenumber,
|
||||
rangenumber+new_content_len,
|
||||
endpos,
|
||||
client->content_length,
|
||||
fserve_content_type(path));
|
||||
}
|
||||
@ -445,7 +445,9 @@ int fserve_client_create(client_t *httpclient, char *path)
|
||||
httpclient->respcode = 200;
|
||||
bytes = sock_write (httpclient->con->sock,
|
||||
"HTTP/1.0 200 OK\r\n"
|
||||
"Content-Length: " FORMAT_INT64 "\r\n"
|
||||
"Content-Type: %s\r\n\r\n",
|
||||
client->content_length,
|
||||
fserve_content_type(path));
|
||||
}
|
||||
if(bytes > 0) httpclient->con->sent_bytes = bytes;
|
||||
|
@ -20,8 +20,8 @@ typedef struct _fserve_t
|
||||
client_t *client;
|
||||
|
||||
FILE *file;
|
||||
int ready;
|
||||
int64_t content_length;
|
||||
int ready;
|
||||
struct _fserve_t *next;
|
||||
} fserve_t;
|
||||
|
||||
|
@ -88,6 +88,7 @@ static void _stop_logging(void)
|
||||
{
|
||||
log_close(errorlog);
|
||||
log_close(accesslog);
|
||||
log_close(playlistlog);
|
||||
}
|
||||
|
||||
static void _initialize_subsystems(void)
|
||||
@ -203,7 +204,6 @@ static int _start_logging(void)
|
||||
strerror(errno));
|
||||
_fatal_error(buf);
|
||||
}
|
||||
|
||||
log_set_level(errorlog, config->loglevel);
|
||||
|
||||
if(strcmp(config->access_log, "-")) {
|
||||
|
@ -738,7 +738,6 @@ static void source_init (source_t *source)
|
||||
source->listeners = 0;
|
||||
stats_event_inc (NULL, "sources");
|
||||
stats_event_inc (NULL, "source_total_connections");
|
||||
stats_event (source->mount, "type", source->format->format_description);
|
||||
|
||||
if (source->con)
|
||||
sock_set_blocking (source->con->sock, SOCK_NONBLOCK);
|
||||
|
2
src/yp.c
2
src/yp.c
@ -512,7 +512,7 @@ static ypdata_t *create_yp_entry (source_t *source)
|
||||
break;
|
||||
|
||||
/* ice-* is icecast, icy-* is shoutcast */
|
||||
add_yp_info (yp, "server_type", source->format->format_description, YP_SERVER_TYPE);
|
||||
add_yp_info (yp, "server_type", source->format->contenttype, YP_SERVER_TYPE);
|
||||
if ((s = httpp_getvar(source->parser, "ice-name"))) {
|
||||
add_yp_info (yp, "server_name", s, YP_SERVER_NAME);
|
||||
}
|
||||
|
@ -1,442 +0,0 @@
|
||||
// ResizableDialog.cpp : implementation file
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2000 by Paolo Messina
|
||||
// (ppescher@yahoo.com)
|
||||
//
|
||||
// Free for non-commercial use.
|
||||
// You may change the code to your needs,
|
||||
// provided that credits to the original
|
||||
// author is given in the modified files.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "ResizableDialog.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
#define new DEBUG_NEW
|
||||
#undef THIS_FILE
|
||||
static char THIS_FILE[] = __FILE__;
|
||||
#endif
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CResizableDialog
|
||||
|
||||
inline void CResizableDialog::Construct()
|
||||
{
|
||||
m_bInitDone = FALSE;
|
||||
|
||||
m_bUseMinTrack = TRUE;
|
||||
m_bUseMaxTrack = FALSE;
|
||||
m_bUseMaxRect = FALSE;
|
||||
|
||||
m_bShowGrip = TRUE;
|
||||
|
||||
m_bEnableSaveRestore = FALSE;
|
||||
|
||||
m_szGripSize.cx = GetSystemMetrics(SM_CXVSCROLL);
|
||||
m_szGripSize.cy = GetSystemMetrics(SM_CYHSCROLL);
|
||||
}
|
||||
|
||||
CResizableDialog::CResizableDialog()
|
||||
{
|
||||
Construct();
|
||||
}
|
||||
|
||||
CResizableDialog::CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd)
|
||||
: CDialog(nIDTemplate, pParentWnd)
|
||||
{
|
||||
Construct();
|
||||
}
|
||||
|
||||
CResizableDialog::CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd)
|
||||
: CDialog(lpszTemplateName, pParentWnd)
|
||||
{
|
||||
Construct();
|
||||
}
|
||||
|
||||
CResizableDialog::~CResizableDialog()
|
||||
{
|
||||
// for safety
|
||||
m_arrLayout.RemoveAll();
|
||||
}
|
||||
|
||||
|
||||
BEGIN_MESSAGE_MAP(CResizableDialog, CDialog)
|
||||
//{{AFX_MSG_MAP(CResizableDialog)
|
||||
ON_WM_NCHITTEST()
|
||||
ON_WM_GETMINMAXINFO()
|
||||
ON_WM_SIZE()
|
||||
ON_WM_DESTROY()
|
||||
ON_WM_PAINT()
|
||||
//}}AFX_MSG_MAP
|
||||
END_MESSAGE_MAP()
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CResizableDialog message handlers
|
||||
|
||||
|
||||
BOOL CResizableDialog::OnInitDialog()
|
||||
{
|
||||
CDialog::OnInitDialog();
|
||||
|
||||
UpdateGripPos();
|
||||
|
||||
// gets the template size as the min track size
|
||||
CRect rc;
|
||||
GetWindowRect(&rc);
|
||||
m_ptMinTrackSize.x = rc.Width();
|
||||
m_ptMinTrackSize.y = rc.Height();
|
||||
|
||||
m_bInitDone = TRUE;
|
||||
|
||||
return TRUE; // return TRUE unless you set the focus to a control
|
||||
// EXCEPTION: OCX Property Pages should return FALSE
|
||||
}
|
||||
|
||||
void CResizableDialog::OnDestroy()
|
||||
{
|
||||
CDialog::OnDestroy();
|
||||
|
||||
if (m_bEnableSaveRestore)
|
||||
SaveWindowRect();
|
||||
|
||||
// remove old windows
|
||||
m_arrLayout.RemoveAll();
|
||||
}
|
||||
|
||||
void CResizableDialog::OnPaint()
|
||||
{
|
||||
CPaintDC dc(this); // device context for painting
|
||||
|
||||
if (m_bShowGrip && !IsZoomed())
|
||||
{
|
||||
// draw size-grip
|
||||
dc.DrawFrameControl(&m_rcGripRect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP);
|
||||
}
|
||||
}
|
||||
|
||||
void CResizableDialog::OnSize(UINT nType, int cx, int cy)
|
||||
{
|
||||
CWnd::OnSize(nType, cx, cy);
|
||||
|
||||
if (nType == SIZE_MAXHIDE || nType == SIZE_MAXSHOW)
|
||||
return; // arrangement not needed
|
||||
|
||||
if (m_bInitDone)
|
||||
{
|
||||
ArrangeLayout();
|
||||
}
|
||||
}
|
||||
|
||||
UINT CResizableDialog::OnNcHitTest(CPoint point)
|
||||
{
|
||||
CPoint pt = point;
|
||||
ScreenToClient(&pt);
|
||||
|
||||
// if in size grip and in client area
|
||||
if (m_bShowGrip && m_rcGripRect.PtInRect(pt) &&
|
||||
pt.x >= 0 && pt.y >= 0)
|
||||
return HTBOTTOMRIGHT;
|
||||
|
||||
return CDialog::OnNcHitTest(point);
|
||||
}
|
||||
|
||||
void CResizableDialog::OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI)
|
||||
{
|
||||
if (!m_bInitDone)
|
||||
return;
|
||||
|
||||
if (m_bUseMinTrack)
|
||||
lpMMI->ptMinTrackSize = m_ptMinTrackSize;
|
||||
|
||||
if (m_bUseMaxTrack)
|
||||
lpMMI->ptMaxTrackSize = m_ptMaxTrackSize;
|
||||
|
||||
if (m_bUseMaxRect)
|
||||
{
|
||||
lpMMI->ptMaxPosition = m_ptMaxPos;
|
||||
lpMMI->ptMaxSize = m_ptMaxSize;
|
||||
}
|
||||
}
|
||||
|
||||
// layout functions
|
||||
|
||||
void CResizableDialog::AddAnchor(HWND wnd, CSize tl_type, CSize br_type)
|
||||
{
|
||||
ASSERT(wnd != NULL && ::IsWindow(wnd));
|
||||
ASSERT(::IsChild(*this, wnd));
|
||||
ASSERT(tl_type != NOANCHOR);
|
||||
|
||||
// get control's window class
|
||||
|
||||
CString st;
|
||||
GetClassName(wnd, st.GetBufferSetLength(MAX_PATH), MAX_PATH);
|
||||
st.ReleaseBuffer();
|
||||
st.MakeUpper();
|
||||
|
||||
// add the style 'clipsiblings' to a GroupBox
|
||||
// to avoid unnecessary repainting of controls inside
|
||||
if (st == "BUTTON")
|
||||
{
|
||||
DWORD style = GetWindowLong(wnd, GWL_STYLE);
|
||||
if (style & BS_GROUPBOX)
|
||||
SetWindowLong(wnd, GWL_STYLE, style | WS_CLIPSIBLINGS);
|
||||
}
|
||||
|
||||
// wnd classes that don't redraw client area correctly
|
||||
// when the hor scroll pos changes due to a resizing
|
||||
BOOL hscroll = FALSE;
|
||||
if (st == "LISTBOX")
|
||||
hscroll = TRUE;
|
||||
|
||||
// wnd classes that need refresh when resized
|
||||
BOOL refresh = FALSE;
|
||||
if (st == "STATIC")
|
||||
{
|
||||
DWORD style = GetWindowLong(wnd, GWL_STYLE);
|
||||
|
||||
switch (style & SS_TYPEMASK)
|
||||
{
|
||||
case SS_LEFT:
|
||||
case SS_CENTER:
|
||||
case SS_RIGHT:
|
||||
// word-wrapped text needs refresh
|
||||
refresh = TRUE;
|
||||
}
|
||||
|
||||
// centered images or text need refresh
|
||||
if (style & SS_CENTERIMAGE)
|
||||
refresh = TRUE;
|
||||
|
||||
// simple text never needs refresh
|
||||
if (style & SS_TYPEMASK == SS_SIMPLE)
|
||||
refresh = FALSE;
|
||||
}
|
||||
|
||||
// get dialog's and control's rect
|
||||
CRect wndrc, objrc;
|
||||
|
||||
GetClientRect(&wndrc);
|
||||
::GetWindowRect(wnd, &objrc);
|
||||
ScreenToClient(&objrc);
|
||||
|
||||
CSize tl_margin, br_margin;
|
||||
|
||||
if (br_type == NOANCHOR)
|
||||
br_type = tl_type;
|
||||
|
||||
// calculate margin for the top-left corner
|
||||
|
||||
tl_margin.cx = objrc.left - wndrc.Width() * tl_type.cx / 100;
|
||||
tl_margin.cy = objrc.top - wndrc.Height() * tl_type.cy / 100;
|
||||
|
||||
// calculate margin for the bottom-right corner
|
||||
|
||||
br_margin.cx = objrc.right - wndrc.Width() * br_type.cx / 100;
|
||||
br_margin.cy = objrc.bottom - wndrc.Height() * br_type.cy / 100;
|
||||
|
||||
// add to the list
|
||||
Layout obj(wnd, tl_type, tl_margin, br_type, br_margin, hscroll, refresh);
|
||||
m_arrLayout.Add(obj);
|
||||
}
|
||||
|
||||
void CResizableDialog::ArrangeLayout()
|
||||
{
|
||||
// update size-grip
|
||||
InvalidateRect(&m_rcGripRect);
|
||||
UpdateGripPos();
|
||||
InvalidateRect(&m_rcGripRect);
|
||||
|
||||
// init some vars
|
||||
CRect wndrc;
|
||||
GetClientRect(&wndrc);
|
||||
|
||||
int i, count = m_arrLayout.GetSize();
|
||||
HDWP hdwp = BeginDeferWindowPos(count);
|
||||
|
||||
for (i=0; i<count; ++i)
|
||||
{
|
||||
Layout& obj = m_arrLayout[i];
|
||||
|
||||
CRect objrc, newrc;
|
||||
CWnd* wnd = CWnd::FromHandle(obj.hwnd); // temporary solution
|
||||
|
||||
wnd->GetWindowRect(&objrc);
|
||||
ScreenToClient(&objrc);
|
||||
|
||||
// calculate new top-left corner
|
||||
|
||||
newrc.left = obj.tl_margin.cx + wndrc.Width() * obj.tl_type.cx / 100;
|
||||
newrc.top = obj.tl_margin.cy + wndrc.Height() * obj.tl_type.cy / 100;
|
||||
|
||||
// calculate new bottom-right corner
|
||||
|
||||
newrc.right = obj.br_margin.cx + wndrc.Width() * obj.br_type.cx / 100;
|
||||
newrc.bottom = obj.br_margin.cy + wndrc.Height() * obj.br_type.cy / 100;
|
||||
|
||||
if (!newrc.EqualRect(&objrc))
|
||||
{
|
||||
if (obj.adj_hscroll)
|
||||
{
|
||||
// needs repainting, due to horiz scrolling
|
||||
int diff = newrc.Width() - objrc.Width();
|
||||
int max = wnd->GetScrollLimit(SB_HORZ);
|
||||
|
||||
obj.need_refresh = FALSE;
|
||||
if (max > 0 && wnd->GetScrollPos(SB_HORZ) > max - diff)
|
||||
{
|
||||
obj.need_refresh = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
// set flags
|
||||
DWORD flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREPOSITION;
|
||||
if (newrc.TopLeft() == objrc.TopLeft())
|
||||
flags |= SWP_NOMOVE;
|
||||
if (newrc.Size() == objrc.Size())
|
||||
flags |= SWP_NOSIZE;
|
||||
|
||||
DeferWindowPos(hdwp, obj.hwnd, NULL, newrc.left, newrc.top,
|
||||
newrc.Width(), newrc.Height(), flags);
|
||||
}
|
||||
}
|
||||
// go re-arrange child windows
|
||||
EndDeferWindowPos(hdwp);
|
||||
|
||||
// refresh those that need
|
||||
for (i=0; i<count; ++i)
|
||||
{
|
||||
Layout& obj = m_arrLayout[i];
|
||||
CWnd* wnd = CWnd::FromHandle(obj.hwnd); // temporary solution
|
||||
|
||||
if (obj.need_refresh)
|
||||
{
|
||||
wnd->Invalidate();
|
||||
wnd->UpdateWindow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CResizableDialog::UpdateGripPos()
|
||||
{
|
||||
// size-grip goes bottom right in the client area
|
||||
|
||||
GetClientRect(&m_rcGripRect);
|
||||
|
||||
m_rcGripRect.left = m_rcGripRect.right - m_szGripSize.cx;
|
||||
m_rcGripRect.top = m_rcGripRect.bottom - m_szGripSize.cy;
|
||||
}
|
||||
|
||||
// protected members
|
||||
|
||||
void CResizableDialog::ShowSizeGrip(BOOL bShow)
|
||||
{
|
||||
if (m_bShowGrip != bShow)
|
||||
{
|
||||
m_bShowGrip = bShow;
|
||||
InvalidateRect(&m_rcGripRect);
|
||||
}
|
||||
}
|
||||
|
||||
void CResizableDialog::SetMaximizedRect(const CRect& rc)
|
||||
{
|
||||
m_bUseMaxRect = TRUE;
|
||||
|
||||
m_ptMaxPos = rc.TopLeft();
|
||||
m_ptMaxSize.x = rc.Width();
|
||||
m_ptMaxSize.y = rc.Height();
|
||||
}
|
||||
|
||||
void CResizableDialog::ResetMaximizedRect()
|
||||
{
|
||||
m_bUseMaxRect = FALSE;
|
||||
}
|
||||
|
||||
void CResizableDialog::SetMinTrackSize(const CSize& size)
|
||||
{
|
||||
m_bUseMinTrack = TRUE;
|
||||
|
||||
m_ptMinTrackSize.x = size.cx;
|
||||
m_ptMinTrackSize.y = size.cy;
|
||||
}
|
||||
|
||||
void CResizableDialog::ResetMinTrackSize()
|
||||
{
|
||||
m_bUseMinTrack = FALSE;
|
||||
}
|
||||
|
||||
void CResizableDialog::SetMaxTrackSize(const CSize& size)
|
||||
{
|
||||
m_bUseMaxTrack = TRUE;
|
||||
|
||||
m_ptMaxTrackSize.x = size.cx;
|
||||
m_ptMaxTrackSize.y = size.cy;
|
||||
}
|
||||
|
||||
void CResizableDialog::ResetMaxTrackSize()
|
||||
{
|
||||
m_bUseMaxTrack = FALSE;
|
||||
}
|
||||
|
||||
// NOTE: this must be called after all the other settings
|
||||
// to have the dialog and its controls displayed properly
|
||||
void CResizableDialog::EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry)
|
||||
{
|
||||
m_sSection = pszSection;
|
||||
m_sEntry = pszEntry;
|
||||
|
||||
m_bEnableSaveRestore = TRUE;
|
||||
|
||||
LoadWindowRect();
|
||||
}
|
||||
|
||||
|
||||
// used to save/restore window's size and position
|
||||
// either in the registry or a private .INI file
|
||||
// depending on your application settings
|
||||
|
||||
#define PROFILE_FMT _T("%d,%d,%d,%d,%d,%d")
|
||||
|
||||
void CResizableDialog::SaveWindowRect()
|
||||
{
|
||||
CString data;
|
||||
WINDOWPLACEMENT wp;
|
||||
|
||||
ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
|
||||
wp.length = sizeof(WINDOWPLACEMENT);
|
||||
GetWindowPlacement(&wp);
|
||||
|
||||
RECT& rc = wp.rcNormalPosition; // alias
|
||||
|
||||
data.Format(PROFILE_FMT, rc.left, rc.top,
|
||||
rc.right, rc.bottom, wp.showCmd, wp.flags);
|
||||
|
||||
AfxGetApp()->WriteProfileString(m_sSection, m_sEntry, data);
|
||||
}
|
||||
|
||||
void CResizableDialog::LoadWindowRect()
|
||||
{
|
||||
CString data;
|
||||
WINDOWPLACEMENT wp;
|
||||
|
||||
data = AfxGetApp()->GetProfileString(m_sSection, m_sEntry);
|
||||
|
||||
if (data.IsEmpty()) // never saved before
|
||||
return;
|
||||
|
||||
ZeroMemory(&wp, sizeof(WINDOWPLACEMENT));
|
||||
wp.length = sizeof(WINDOWPLACEMENT);
|
||||
GetWindowPlacement(&wp);
|
||||
|
||||
RECT& rc = wp.rcNormalPosition; // alias
|
||||
|
||||
if (_stscanf(data, PROFILE_FMT, &rc.left, &rc.top,
|
||||
&rc.right, &rc.bottom, &wp.showCmd, &wp.flags) == 6)
|
||||
{
|
||||
SetWindowPlacement(&wp);
|
||||
}
|
||||
}
|
@ -1,173 +0,0 @@
|
||||
#if !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_)
|
||||
#define AFX_RESIZABLEDIALOG_H__INCLUDED_
|
||||
|
||||
#if _MSC_VER > 1000
|
||||
#pragma once
|
||||
#endif // _MSC_VER > 1000
|
||||
|
||||
// ResizableDialog.h : header file
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Copyright (C) 2000 by Paolo Messina
|
||||
// (ppescher@yahoo.com)
|
||||
//
|
||||
// Free for non-commercial use.
|
||||
// You may change the code to your needs,
|
||||
// provided that credits to the original
|
||||
// author is given in the modified files.
|
||||
//
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#include <afxtempl.h>
|
||||
#include <afxwin.h>
|
||||
|
||||
// useful compatibility constants (the only one required is NOANCHOR)
|
||||
|
||||
#if !defined(__SIZE_ANCHORS_)
|
||||
#define __SIZE_ANCHORS_
|
||||
|
||||
const CSize
|
||||
NOANCHOR(-1,-1),
|
||||
TOP_LEFT(0,0), TOP_CENTER(50,0), TOP_RIGHT(100,0),
|
||||
MIDDLE_LEFT(0,50), MIDDLE_CENTER(50,50), MIDDLE_RIGHT(100,50),
|
||||
BOTTOM_LEFT(0,100), BOTTOM_CENTER(50,100), BOTTOM_RIGHT(100,100);
|
||||
|
||||
#endif // !defined(__SIZE_ANCHORS_)
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// CResizableDialog window
|
||||
|
||||
class CResizableDialog : public CDialog
|
||||
{
|
||||
|
||||
// Construction
|
||||
public:
|
||||
CResizableDialog();
|
||||
CResizableDialog(UINT nIDTemplate, CWnd* pParentWnd = NULL);
|
||||
CResizableDialog(LPCTSTR lpszTemplateName, CWnd* pParentWnd = NULL);
|
||||
|
||||
// Attributes
|
||||
private:
|
||||
// flags
|
||||
BOOL m_bShowGrip;
|
||||
BOOL m_bUseMaxTrack;
|
||||
BOOL m_bUseMinTrack;
|
||||
BOOL m_bUseMaxRect;
|
||||
BOOL m_bEnableSaveRestore;
|
||||
|
||||
// internal status
|
||||
CString m_sSection; // section name and
|
||||
CString m_sEntry; // entry for save/restore
|
||||
|
||||
BOOL m_bInitDone; // if all internal vars initialized
|
||||
|
||||
SIZE m_szGripSize; // set at construction time
|
||||
|
||||
CRect m_rcGripRect; // current pos of grip
|
||||
|
||||
POINT m_ptMinTrackSize; // min tracking size
|
||||
POINT m_ptMaxTrackSize; // max tracking size
|
||||
POINT m_ptMaxPos; // maximized position
|
||||
POINT m_ptMaxSize; // maximized size
|
||||
|
||||
class Layout
|
||||
{
|
||||
public:
|
||||
HWND hwnd;
|
||||
|
||||
BOOL adj_hscroll;
|
||||
BOOL need_refresh;
|
||||
|
||||
// upper-left corner
|
||||
CSize tl_type;
|
||||
CSize tl_margin;
|
||||
|
||||
// bottom-right corner
|
||||
CSize br_type;
|
||||
CSize br_margin;
|
||||
|
||||
public:
|
||||
Layout()
|
||||
: hwnd(NULL), adj_hscroll(FALSE), need_refresh(FALSE),
|
||||
tl_type(0,0), tl_margin(0,0),
|
||||
br_type(0,0), br_margin(0,0)
|
||||
{
|
||||
};
|
||||
|
||||
Layout(HWND hw, SIZE tl_t, SIZE tl_m,
|
||||
SIZE br_t, SIZE br_m, BOOL hscroll, BOOL refresh)
|
||||
{
|
||||
hwnd = hw;
|
||||
|
||||
adj_hscroll = hscroll;
|
||||
need_refresh = refresh;
|
||||
|
||||
tl_type = tl_t;
|
||||
tl_margin = tl_m;
|
||||
|
||||
br_type = br_t;
|
||||
br_margin = br_m;
|
||||
};
|
||||
};
|
||||
|
||||
CArray<Layout, Layout&> m_arrLayout; // list of repositionable controls
|
||||
|
||||
// Operations
|
||||
public:
|
||||
|
||||
// Overrides
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CResizableDialog)
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Implementation
|
||||
public:
|
||||
virtual ~CResizableDialog();
|
||||
|
||||
// used internally
|
||||
private:
|
||||
void Construct();
|
||||
void LoadWindowRect();
|
||||
void SaveWindowRect();
|
||||
void ArrangeLayout();
|
||||
void UpdateGripPos();
|
||||
|
||||
// callable from derived classes
|
||||
//protected:
|
||||
public:
|
||||
void AddAnchor(HWND wnd, CSize tl_type,
|
||||
CSize br_type = NOANCHOR); // add anchors to a control
|
||||
void AddAnchor(UINT ctrl_ID, CSize tl_type,
|
||||
CSize br_type = NOANCHOR) // add anchors to a control
|
||||
{
|
||||
AddAnchor(::GetDlgItem(*this, ctrl_ID), tl_type, br_type);
|
||||
};
|
||||
void ShowSizeGrip(BOOL bShow); // show or hide the size grip
|
||||
void SetMaximizedRect(const CRect& rc); // set window rect when maximized
|
||||
void ResetMaximizedRect(); // reset to default maximized rect
|
||||
void SetMinTrackSize(const CSize& size); // set minimum tracking size
|
||||
void ResetMinTrackSize(); // reset to default minimum tracking size
|
||||
void SetMaxTrackSize(const CSize& size); // set maximum tracking size
|
||||
void ResetMaxTrackSize(); // reset to default maximum tracking size
|
||||
void EnableSaveRestore(LPCTSTR pszSection, LPCTSTR pszEntry); // section and entry in app's profile
|
||||
|
||||
// Generated message map functions
|
||||
protected:
|
||||
//{{AFX_MSG(CResizableDialog)
|
||||
virtual BOOL OnInitDialog();
|
||||
afx_msg UINT OnNcHitTest(CPoint point);
|
||||
afx_msg void OnGetMinMaxInfo(MINMAXINFO FAR* lpMMI);
|
||||
afx_msg void OnSize(UINT nType, int cx, int cy);
|
||||
afx_msg void OnDestroy();
|
||||
afx_msg void OnPaint();
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
};
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//{{AFX_INSERT_LOCATION}}
|
||||
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
|
||||
|
||||
#endif // !defined(AFX_RESIZABLEDIALOG_H__INCLUDED_)
|
Loading…
Reference in New Issue
Block a user