/*
* util.cc
*
* Copyright (C) 1995-2001 Kenichi Kourai
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with qvwm; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef HAVE_VFORK_H
#include <vfork.h>
#endif
#ifndef HAVE_USLEEP
#include <sys/time.h>
#endif
#ifdef __EMX__
#include <process.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xlocale.h>
#include <X11/xpm.h>
#include "main.h"
#include "misc.h"
#include "qvwm.h"
#include "menu.h"
#include "util.h"
#include "qvwmrc.h"
#include "desktop.h"
#include "timer.h"
#include "callback.h"
#include "audio_wav.h"
#include "audio_au.h"
#include "exec.h"
#include "paging.h"
#include "pager.h"
#include "anim_image.h"
#include "pixmap_image.h"
#ifdef USE_IMLIB
#include "extra_image.h"
#endif
/*
* Gravity offset for frame decoration.
*/
Point GravityOffset[] = {
Point(CENTER, CENTER), // ForgetGravity
Point(WEST, NORTH), // NorthWestGravity
Point(CENTER, NORTH), // NorthGravity
Point(EAST, NORTH), // NorthEastGravity
Point(WEST, CENTER), // WestGravity
Point(CENTER, CENTER), // CenterGravity
Point(EAST, CENTER), // EastGravity
Point(WEST, SOUTH), // SouthWestGravity
Point(CENTER, SOUTH), // SouthGravity
Point(EAST, SOUTH), // SouthEastGravity
Point(CENTER, CENTER), // StaticGravity
};
List<ExecPending> ExecPending::m_pendingList;
/*
* InRect --
* Return True if the point is in a given rectangle.
*/
Bool InRect(const Point& pt, const Rect& rc)
{
if (pt.x < rc.x)
return False;
if (pt.x >= rc.x + rc.width)
return False;
if (pt.y < rc.y)
return False;
if (pt.y >= rc.y + rc.height)
return False;
return True;
}
/*
* Intersect --
* Return True if two rectangles intersect each other.
*/
Bool Intersect(const Rect& rc1, const Rect& rc2)
{
if (InRect(Point(rc1.x, rc1.y), rc2) ||
InRect(Point(rc1.x + rc1.width - 1, rc1.y), rc2) ||
InRect(Point(rc1.x, rc1.y + rc1.height - 1), rc2) ||
InRect(Point(rc1.x + rc1.width - 1, rc1.y + rc1.height - 1), rc2) ||
InRect(Point(rc2.x, rc2.y), rc1) ||
InRect(Point(rc2.x + rc2.width - 1, rc2.y), rc1) ||
InRect(Point(rc2.x, rc2.y + rc2.height - 1), rc1) ||
InRect(Point(rc2.x + rc2.width - 1, rc2.y + rc2.height - 1), rc1))
return True;
return False;
}
/*
* IsDoubleClick --
* Return True if the click is decided as double click from time and space.
*/
Bool IsDoubleClick(Time prevTime, Time clickTime, const Point& ptPrev,
const Point& ptClick)
{
Rect rcPrev;
if (clickTime - prevTime > (unsigned int)DoubleClickTime)
return False;
rcPrev = Rect(ptPrev.x-DoubleClickRange, ptPrev.y-DoubleClickRange,
ptPrev.x+DoubleClickRange, ptPrev.y+DoubleClickRange);
if (!InRect(ptClick, rcPrev))
return False;
return True;
}
/*
* GetMenuItemNum --
* Return the number of elements in mItem.
*/
int GetMenuItemNum(const MenuElem* mItem)
{
int num = 0;
while (mItem) {
num++;
mItem = mItem->next;
}
return num;
}
QvImage* CreateImageFromFile(char* file, Timer* timer)
{
switch (file[0]) {
case '/':
return CreateImage(file, timer);
case '~':
case '$':
{
char* filename = ExtractPathName(file);
return CreateImage(filename, timer);
}
default:
{
QvImage* img;
char* paths = new char[strlen(ImagePath) + 1 + strlen(IMGDIR) + 1];
int filelen = strlen(file);
sprintf(paths, "%s:%s", ImagePath, IMGDIR);
char* pathname = strtok(paths, ":");
while (pathname) {
char* realpath = ExtractPathName(pathname);
int len = strlen(realpath) + 1 + filelen;
char* filename = new char[len + 1];
sprintf(filename, "%s/%s", realpath, file);
delete [] realpath;
img = CreateImage(filename, timer);
delete [] filename;
if (img) {
delete [] paths;
return img;
}
pathname = strtok(NULL, ":");
}
delete [] paths;
QvwmError("Can't find image '%s'", file);
return NULL;
}
}
}
QvImage* CreateImage(char* filename, Timer* timer)
{
QvImage* img;
struct stat st;
int len = strlen(filename);
#ifdef __EMX__
if (stat(__XOS2RedirRoot(filename), &st) == 0) {
#else
if (stat(filename, &st) == 0) {
#endif
if (strcmp(&filename[len - 4], ".ani") == 0)
img = new AnimImage(filename, timer);
else {
#ifdef USE_IMLIB
img = new ExtraImage(filename);
#else
if (strcmp(&filename[len - 4], ".xpm") == 0)
img = new PixmapImage(filename);
else {
QvwmError("unsupported image file: %s", filename);
img = NULL;
}
#endif // USE_IMLIB
}
if (img && img->GetError()) {
QvImage::Destroy(img);
img = NULL;
}
return img;
}
return NULL;
}
/*
* GetFixName --
*/
char* GetFixName(XFontSet& fs, const char* name, int width)
{
const int dotWidth = 11;
int len, curWidth;
char *str;
XRectangle ink, log;
len = strlen(name);
// str is enough short
if ((curWidth = XmbTextExtents(fs, name, len, &ink, &log)) <= width){
str = new char[len + 1];
strcpy(str, name);
return str;
}
// can't write even "..."
if (width <= dotWidth) {
str = new char[1];
*str = '\0';
return str;
}
if (curWidth < (width-dotWidth) * 2) {
while (XmbTextExtents(fs, name, len, &ink, &log) > width - dotWidth)
if (--len == 0) {
str = new char[1];
*str = '\0';
return str;
}
}
else {
len = 0;
while (XmbTextExtents(fs, name, len, &ink, &log) < width - dotWidth)
len++;
len--;
}
str = new char[len + 1];
strncpy(str, name, len);
str[len] = '\0';
return str;
}
/*
* MappedNotOverride --
* Return True if the window attribute doesn't include override_redirect.
*/
Bool MappedNotOverride(Window w, long* state)
{
XWindowAttributes attr;
Atom atype;
int aformat;
unsigned long nitems, bytes_remain;
unsigned char* prop;
if(!XGetWindowAttributes(display, w, &attr))
return False;
*state = NormalState;
if (XGetWindowProperty(display, w, _XA_WM_STATE, 0, 3, False, _XA_WM_STATE,
&atype, &aformat, &nitems, &bytes_remain, &prop)
==Success) {
if (prop != NULL) {
*state = *(long *)prop;
XFree(prop);
}
}
return (((*state == IconicState) || (attr.map_state != IsUnmapped))
&& !attr.override_redirect);
}
GC CreateTileGC(Drawable d)
{
char data[] = {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa};
Pixmap pattern;
GC gcTile;
pattern = XCreateBitmapFromData(display, d, data, 8, 8);
gcTile = XCreateGC(display, d, 0, 0);
XSetFillStyle(display, gcTile, FillStippled);
XSetStipple(display, gcTile, pattern);
XFreePixmap(display, pattern);
return gcTile;
}
/*
* DarkenScreen --
* Darken whole screen.
*/
void DarkenScreen()
{
GC gcDark;
gcDark = CreateTileGC(root);
XSetSubwindowMode(display, gcDark, IncludeInferiors);
XSetForeground(display, gcDark, black.pixel);
XFillRectangle(display, root, gcDark,
0, 0,
DisplayWidth(display, screen),
DisplayHeight(display, screen));
XFreeGC(display, gcDark);
}
/*
* RefreshScreen --
* Refresh whole screen.
*/
void RefreshScreen()
{
Window w;
w = XCreateSimpleWindow(display, root,
0, 0,
DisplayWidth(display, screen),
DisplayHeight(display, screen),
0, black.pixel, white.pixel);
XMapWindow(display, w);
XFlush(display);
XDestroyWindow(display, w);
}
// tcsh does not work...
#define SHELL "/bin/sh"
pid_t ExecCommand(char* exec)
{
pid_t pid;
if (strncmp(exec, "EXEC ", 5) == 0) {
char cmd[256], *argv[16];
int i = 1;
strcpy(cmd, &exec[5]);
argv[0] = strtok(cmd, " ");
while ((argv[i] = strtok(NULL, " ")) != NULL)
i++;
desktop.FinishQvwm(True);
execvp(cmd, argv);
}
else if (strncmp(exec, "PAGE", 4) == 0) {
char cmd[256], *comp;
Point pageoff;
if (exec[4] == '[') {
char *next;
strcpy(cmd, &exec[5]);
// x page offset
next = strtok(cmd, ",");
if (next == NULL) {
QvwmError("Syntax error: first ',' is missing");
QvwmError(" %s", exec);
return -1;
}
pageoff.x = atoi(next) * DisplayWidth(display, screen);
// y page offset
next = strtok(NULL, ",");
if (next == NULL) {
QvwmError("Syntax error: second ',' is missing");
QvwmError(" %s", exec);
return -1;
}
pageoff.y = atoi(next) * DisplayHeight(display, screen);
// compare string
comp = strtok(NULL, "]");
}
else if (strncmp(&exec[4], "CUR[", 4) == 0) {
pageoff = paging->origin;
strcpy(cmd, &exec[8]);
comp = strtok(cmd, "]");
}
else {
QvwmError("Syntax error: %s", exec);
return -1;
}
if (comp == NULL) {
QvwmError("Syntax error: compare string is missing");
QvwmError(" %s", exec);
return -1;
}
// eliminate spaces before the string
while (*comp == ' ')
comp++;
// return if the compare string is not specified
int len = strlen(comp);
if (len == 0) {
QvwmError("Syntax error: compare string is missing");
QvwmError(" %s", exec);
return -1;
}
// eliminate spaces after the compare string
char* ptr = &comp[len - 1];
while (*ptr == ' ' && ptr > comp)
ptr--;
*(ptr + 1) = '\0';
ExecPending* pending = new ExecPending(pageoff, comp);
ExecPending::m_pendingList.InsertTail(pending);
char* excmd = strchr(exec, ']');
excmd++;
while (*excmd == ' ')
excmd++;
if (*excmd == '\0') {
QvwmError("Syntax error: external command is not specified");
QvwmError(" %s", exec);
return -1;
}
exec = excmd;
}
char cmd[1024];
if (strlen(exec) + 5 >= sizeof(cmd)) {
QvwmError("too long command string: '%s'", exec);
return -1;
}
sprintf(cmd, "exec %s", exec);
if (HourGlassTime > 0) {
XDefineCursor(display, root, cursor[BGWORK]);
timer->ResetTimeout(new GlobalCallback(&RestoreCursor));
timer->SetTimeout(HourGlassTime, new GlobalCallback(&RestoreCursor));
}
#ifdef __EMX__
if (spawnl(P_NOWAIT, "cmd.exe", "cmd.exe", "/C", exec, NULL) == -1)
QvwmError("Can't execute '%s'", exec);
#else
if ((pid = vfork()) == 0) {
if (execl(SHELL, SHELL, "-c", cmd, NULL) == -1) {
QvwmError("Can't execute '%s'", &cmd[5]);
_exit(1);
}
}
#endif
return (pid);
}
void RestoreCursor()
{
XDefineCursor(display, root, cursor[SYS]);
}
#define MAX_ENV_NAME 255
#define MAX_USER_NAME 32
/*
* Extract ~ and environment variable in a path name.
*/
char* ExtractPathName(char* name)
{
char* exname;
if (name == NULL)
return NULL;
if (name[0] == '~') {
if (name[1] == '/' || name[1] == '\0') {
char* home = getenv("HOME");
char* dir = &name[1];
if (home == NULL) {
QvwmError("Undefined environment variable HOME\n");
exname = new char[strlen(dir) + 1];
strcpy(exname, dir);
}
else {
exname = new char[strlen(home) + strlen(dir) + 1];
sprintf(exname, "%s%s", home, dir);
}
}
else {
char user[MAX_USER_NAME + 1];
char* dir;
dir = GetNextDelim(&name[1], user, MAX_USER_NAME + 1);
struct passwd* pw = getpwnam(user);
if (pw == NULL) {
if (user[0] != '\0')
QvwmError("No user %s\n", user);
exname = new char[strlen(name) + 1];
strcpy(exname, name);
}
else {
exname = new char[strlen(pw->pw_dir) + strlen(dir) + 1];
sprintf(exname, "%s%s", pw->pw_dir, dir);
}
}
return exname;
}
else if (name[0] == '$') {
char env[MAX_ENV_NAME + 1];
char* dir;
dir = GetNextDelim(&name[1], env, MAX_ENV_NAME + 1);
char* exenv = getenv(env);
if (exenv == NULL) {
if (env[0] != '\0')
QvwmError("No environment variable %s\n", env);
exname = new char[strlen(dir) + 1];
strcpy(exname, dir);
}
else {
exname = new char[strlen(exenv) + strlen(dir) + 1];
sprintf(exname, "%s%s", exenv, dir);
}
return exname;
}
exname = new char[strlen(name) + 1];
strcpy(exname, name);
return exname;
}
// maxSize includes null for termination
char* GetNextDelim(char* path, char* name, int maxSize)
{
int i;
for (i = 0; path[i] != '/' && path[i] != '\0'; i++) {
if (i >= maxSize - 1) {
name[0] = '\0';
QvwmError("Too long before delim: %s\n", path);
return path;
}
name[i] = path[i];
}
name[i] = '\0';
return &path[i];
}
#if !defined(HAVE_USLEEP) && !defined(__EMX__)
#if defined(sun) && !defined(__SVR4)
extern "C" int select(int, fd_set *, fd_set *, fd_set *, struct timeval *);
#endif
void usleep(unsigned long usec)
{
struct timeval tm;
if (usec <= 0)
return;
tm.tv_usec = usec % 1000000;
tm.tv_sec = usec / 1000000;
select(1, NULL, NULL, NULL, &tm);
}
#endif // HAVE_USLEEP
// Get string width excluding "\B" for bold font and "\&" for underscore
int GetRealWidth(XFontSet fs, char* str)
{
XRectangle ink, log;
unsigned int width;
if (*str == '\\' && *(str + 1) == 'B')
str += 2;
char* keyStr = strstr(str, "\\&");
if (keyStr == NULL)
width = XmbTextExtents(fs, str, strlen(str), &ink, &log);
else {
// length before "\&"
width = XmbTextExtents(fs, str, keyStr - str, &ink, &log);
// length after "\&"
width += XmbTextExtents(fs, keyStr + 2, str + strlen(str) - (keyStr + 2),
&ink, &log);
}
return width;
}
// Draw "...\&?..." -> "...?..."
// ~
void DrawRealString(Window w, XFontSet fs, GC gc, int x, int y, char* str)
{
Bool bold = False;
if (*str == '\\' && *(str + 1) == 'B') {
str += 2;
bold = True;
}
char* keyStr = strstr(str, "\\&");
XRectangle ink, log;
int len = strlen(str);
if (keyStr == NULL) {
XmbDrawString(display, w, fs, gc, x, y, str, len);
if (bold)
XmbDrawString(display, w, fs, gc, x, y + 1, str, len);
}
else {
// draw "..."
XmbDrawString(display, w, fs, gc, x, y, str, keyStr - str);
if (bold)
XmbDrawString(display, w, fs, gc, x, y + 1, str, keyStr - str);
// draw "?..."
XmbTextExtents(fs, str, keyStr - str, &ink, &log);
x += log.width;
XmbDrawString(display, w, fs, gc, x, y,
keyStr + 2, str + len - (keyStr + 2));
if (bold)
XmbDrawString(display, w, fs, gc, x, y + 1,
keyStr + 2, str + len - (keyStr + 2));
// draw underscore
XmbTextExtents(fs, keyStr + 2, 1, &ink, &log);
XDrawLine(display, w, gc, x, y + 1, x + log.width, y + 1);
}
}
#define AUDIODEV "/dev/audio"
void PlaySound(char* file, Bool sync)
{
if (file == NULL || file[0] == '\0')
return;
if (!EnableSound)
return;
#ifndef __EMX__
if (!sync) {
pid_t pid = fork();
if (pid)
return;
}
Audio* audio;
int filelen = strlen(file);
int ret;
if (filelen > 4 && strcmp(&file[filelen - 4], ".wav") == 0)
audio = new AudioWav(AUDIODEV);
else if (filelen > 3 && strcmp(&file[filelen - 3], ".au") == 0)
audio = new AudioAu(AUDIODEV);
else {
QvwmError("unsupported extentions of sound file: %s", file);
if (sync)
return;
else
exit(0);
}
// EnableSound may be set to False in Audio constructor
if (!EnableSound) {
delete audio;
if (sync)
return;
else
exit(0);
}
switch (file[0]) {
case '/':
ret = audio->Play(file);
break;
case '~':
case '$':
{
char* filename = ExtractPathName(file);
ret = audio->Play(filename);
delete [] filename;
}
break;
default:
{
char* paths = new char[strlen(SoundPath) + 1 + strlen(SNDDIR) + 1];
sprintf(paths, "%s:%s", SoundPath, SNDDIR);
char* pathname = strtok(paths, ":");
while (pathname) {
char* realpath = ExtractPathName(pathname);
int len = strlen(realpath) + 1 + filelen;
char* filename = new char[len + 1];
sprintf(filename, "%s/%s", realpath, file);
delete [] realpath;
ret = audio->Play(filename);
delete [] filename;
if (ret == AUDIO_FATAL) {
/* do not try audio after that */
EnableSound = False;
break;
}
if (ret != AUDIO_NOFILE)
break;
pathname = strtok(NULL, ":");
}
delete [] paths;
}
break;
}
if (ret == AUDIO_NOFILE)
QvwmError("not found sound file: '%s'", file);
delete audio;
if (!sync)
exit(0);
#endif // __EMX__
}
Bool IsPointerInWindow(const Point& ptRoot)
{
List<Qvwm>::Iterator i(&desktop.GetQvwmList());
for (Qvwm* qvWm = i.GetHead(); qvWm; qvWm = i.GetNext()) {
Rect rcReal = qvWm->GetRect();
rcReal.x -= paging->origin.x;
rcReal.y -= paging->origin.y;
if (InRect(ptRoot, rcReal))
return True;
}
if (UseTaskbar) {
if (InRect(ptRoot, taskBar->GetRect()))
return True;
}
if (UsePager) {
if (InRect(ptRoot, pager->GetRect()))
return True;
}
return False;
}
/*
* CreateColor --
* Allocate color from red, green and blue.
*/
int CreateColor(unsigned short red, unsigned short green,
unsigned short blue, XColor* color, XColor* substitute,
const char* comment)
{
int status;
color->red = red;
color->green = green;
color->blue = blue;
status = XAllocColor(display, colormap, color);
if (status == 0) {
if (comment)
QvwmError("cannot allocate color: %s", comment);
if (substitute)
*color = *substitute;
else
color->pixel = 0;
}
return status;
}
// require is a set of indispensable parameters
Bool SetGeometry(char* str, InternGeom* geom)
{
if (geom == NULL)
return False;
// initialize
geom->rc = Rect(0, 0, 0, 0);
geom->bitmask = XParseGeometry(str, &geom->rc.x, &geom->rc.y,
(unsigned int *)&geom->rc.width,
(unsigned int *)&geom->rc.height);
if (geom->bitmask & XNegative) // EastGravity
geom->gravity.x = EAST;
else if (geom->bitmask & XValue) // WestGravity
geom->gravity.x = WEST;
else // CenterGravity
geom->gravity.x = CENTER;
if (geom->bitmask & YNegative) // SouthGravity
geom->gravity.y = SOUTH;
else if (geom->bitmask & YValue) // NorthGravity
geom->gravity.y = NORTH;
else // CenterGravity
geom->gravity.y = CENTER;
return True;
}
void SetDisplayEnv(const char* displayName)
{
static char envStr[256];
snprintf(envStr, 256, "DISPLAY=%s", displayName);
putenv(envStr);
}
int GetXColorFromName(const char* str, XColor* color)
{
if (strcmp(str, "qvgray") == 0)
*color = gray;
else if (strcmp(str, "qvdarkgray") == 0)
*color = darkGray;
else if (strcmp(str, "qvgrey") == 0)
*color = grey;
else if (strcmp(str, "qvdarkgrey") == 0)
*color = darkGrey;
else if (strcmp(str, "qvblue") == 0)
*color = blue;
else if (strcmp(str, "qvlightblue") == 0)
*color = lightBlue;
else if (strcmp(str, "qvroyalblue") == 0)
*color = royalBlue;
else if (strcmp(str, "qvyellow") == 0)
*color = yellow;
else if (strcmp(str, "qvlightyellow") == 0)
*color = lightYellow;
else if (strcmp(str, "qvgray95") == 0) {
if (gray95.pixel == 0)
CreateColor(0xbefb, 0xbefb, 0xbefb, &gray95, &white, "qvgray95");
*color = gray95;
}
else if (strcmp(str, "qvdarkgray95") == 0) {
if (darkGray95.pixel == 0)
CreateColor(0x79e7, 0x7df7, 0x79e7, &darkGray95, &black,
"qvdarkgray95");
*color = darkGray95;
}
else if (strcmp(str, "qvlightgray95") == 0) {
if (lightGray95.pixel == 0)
CreateColor(0xdf7d, 0xdf7d, 0xdf7d, &lightGray95, &gray95,
"qvlightgray95");
*color = lightGray95;
}
else if (strcmp(str, "qvgrey95") == 0) { // not qvgray!
if (grey95.pixel == 0)
CreateColor(0xb6da, 0xb2ca, 0xb6da, &grey95, &gray95, "qvgrey95");
*color = grey95;
}
else if (strcmp(str, "qvblue95") == 0) {
if (blue95.pixel == 0)
CreateColor(0x0000, 0x0000, 0x79e7, &blue95, &black, "qvblue95");
*color = blue95;
}
else if (strcmp(str, "qvlightblue95") == 0) {
if (lightBlue95.pixel == 0)
CreateColor(0x0820, 0x8207, 0xcf3c, &lightBlue95, &blue95,
"qvlightblue95");
*color = lightBlue95;
}
else if (strcmp(str, "qvgreen95") == 0) {
if (green95.pixel == 0)
CreateColor(0x0000, 0x7df7, 0x79e7, &green95, &blue95,
"qvgreen95");
*color = green95;
}
else if (strcmp(str, "qvyellow95") == 0) {
if (yellow95.pixel == 0)
CreateColor(0xffff, 0xffff, 0xdf7d, &yellow95, &white,
"qvyellow95");
*color = yellow95;
}
else if (XParseColor(display, colormap, str, color) != 0)
XAllocColor(display, colormap, color);
else
return -1;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1