You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
7.2 KiB
248 lines
7.2 KiB
/* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
|
|
* (c)DWTFYW
|
|
*
|
|
* This program is free software. It comes without any warranty, to
|
|
* the extent permitted by applicable law. You can redistribute it
|
|
* and/or modify it under the terms of the Do What The Fuck You Want
|
|
* To Public License, Version 2, as published by Sam Hocevar. See
|
|
* http://sam.zoy.org/wtfpl/COPYING for more details.
|
|
*/
|
|
#include <QDebug>
|
|
//#include <QtCore>
|
|
|
|
#include "xcrxcur.h"
|
|
|
|
#include <QApplication>
|
|
#include <QStringList>
|
|
#include <QStyle>
|
|
#include <QTextCodec>
|
|
#include <QTextStream>
|
|
|
|
#include <QX11Info>
|
|
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xcursor/Xcursor.h>
|
|
#include <X11/extensions/Xfixes.h>
|
|
|
|
|
|
/*
|
|
static QImage autoCropImage (const QImage &image) {
|
|
// Compute an autocrop rectangle for the image
|
|
QRect r(image.rect().bottomRight(), image.rect().topLeft());
|
|
const quint32 *pixels = reinterpret_cast<const quint32*>(image.bits());
|
|
for (int y = 0; y < image.height(); y++) {
|
|
for (int x = 0; x < image.width(); x++) {
|
|
if (*(pixels++)) {
|
|
if (x < r.left()) r.setLeft(x);
|
|
if (x > r.right()) r.setRight(x);
|
|
if (y < r.top()) r.setTop(y);
|
|
if (y > r.bottom()) r.setBottom(y);
|
|
}
|
|
}
|
|
}
|
|
// Normalize the rectangle
|
|
return image.copy(r.normalized());
|
|
}
|
|
*/
|
|
|
|
|
|
inline static quint32 getDW (const void *data) {
|
|
const quint8 *d = (const quint8 *)data;
|
|
d += 3;
|
|
quint32 res = 0;
|
|
for (int f = 4; f > 0; f--, d--) {
|
|
res <<= 8;
|
|
res |= *d;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
static quint32 baGetDW (QByteArray &ba, int &pos) {
|
|
const uchar *d = (const uchar *)ba.constData();
|
|
d += pos+3;
|
|
pos += 4;
|
|
quint32 res = 0;
|
|
for (int f = 4; f > 0; f--, d--) {
|
|
res <<= 8;
|
|
res |= *d;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
XCursorImageXCur::XCursorImageXCur (const QString &aName, const void *aImgData) : XCursorImage(aName) {
|
|
parseImgData(aImgData);
|
|
}
|
|
|
|
|
|
XCursorImageXCur::~XCursorImageXCur () {
|
|
}
|
|
|
|
|
|
void XCursorImageXCur::parseImgData (const void *aImgData) {
|
|
mIsValid = false;
|
|
if (mImage) delete mImage; mImage = 0;
|
|
const quint32 *data = (const quint32 *)aImgData;
|
|
if (getDW(data) != 36) return; // header size
|
|
if (getDW(data+1) != 0xfffd0002L) return; // magic
|
|
//if (getDW(data+2) != 1) return; // image subtype
|
|
if (getDW(data+3) != 1) return; // version
|
|
mCSize = getDW(data+2);
|
|
data += 4;
|
|
quint32 wdt = getDW(data++); // width
|
|
quint32 hgt = getDW(data++); // height
|
|
if (wdt > 0x7fff) return;
|
|
if (hgt > 0x7fff) return;
|
|
/*
|
|
quint32 xhot = getDW(data++);
|
|
quint32 yhot = getDW(data++);
|
|
assert(xhot <= wdt);
|
|
assert(yhot <= hgt);
|
|
*/
|
|
mXHot = *((const qint32 *)data); data++;
|
|
mYHot = *((const qint32 *)data); data++;
|
|
mDelay = getDW(data++); // milliseconds
|
|
// got to pixels (ARGB)
|
|
QImage img((const uchar *)data, wdt, hgt, QImage::Format_ARGB32_Premultiplied);
|
|
mImage = new QImage(img.copy());
|
|
mIsValid = true;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
XCursorImagesXCur::XCursorImagesXCur (const QDir &aDir, const QString &aName) : XCursorImages(aName, aDir.path()) {
|
|
parseCursorFile(aDir.path()+"/"+aName);
|
|
}
|
|
|
|
|
|
XCursorImagesXCur::XCursorImagesXCur (const QString &aFileName) : XCursorImages("", "") {
|
|
QString name(aFileName);
|
|
if (name.isEmpty() || name.endsWith('/')) return;
|
|
int i = name.lastIndexOf('/');
|
|
QString dir;
|
|
if (i < 0) dir = "./"; else dir = name.left(i);
|
|
name = name.mid(i+1);
|
|
setName(name); setPath(dir);
|
|
parseCursorFile(aFileName);
|
|
}
|
|
|
|
|
|
bool XCursorImagesXCur::parseCursorFile (const QString &fname) {
|
|
//qDebug() << fname;
|
|
qDeleteAll(mList);
|
|
mList.clear();
|
|
QFile fl(fname);
|
|
if (!fl.open(QIODevice::ReadOnly)) return false; // shit!
|
|
QByteArray ba(fl.readAll());
|
|
fl.close();
|
|
if (ba.size() < 4*4) return false; // shit!
|
|
if (ba[0] != 'X' || ba[1] != 'c' || ba[2] != 'u' || ba[3] != 'r') return false; // shit!
|
|
//FIXME: add more checks!
|
|
int pos = 4;
|
|
quint32 hdrSize = baGetDW(ba, pos);
|
|
if (hdrSize < 16) return false; // invalid header size
|
|
quint32 version = baGetDW(ba, pos);
|
|
if (version != 65536) return false; // invalid version
|
|
quint32 ntoc = baGetDW(ba, pos);
|
|
if (!ntoc) return true; // nothing to parse
|
|
if (ntoc >= 65536) return false; // idiot or what?
|
|
quint32 tocEndOfs = hdrSize+(ntoc*(3*4));
|
|
if (tocEndOfs > (quint32)ba.size()) return false; // out of data
|
|
pos = hdrSize;
|
|
// parse TOC
|
|
int cnt = -1;
|
|
bool wasTitle = false, wasAuthor = false, wasLic = false;
|
|
bool wasMail = false, wasSite = false, wasDescr = false, wasIM = false;
|
|
while (ntoc-- > 0) {
|
|
cnt++;
|
|
quint32 type = baGetDW(ba, pos);
|
|
pos += 4; // skip the shit
|
|
quint32 dataOfs = baGetDW(ba, pos);
|
|
if (type == 0xfffd0001) {
|
|
// text
|
|
if (dataOfs < tocEndOfs || dataOfs > (quint32)ba.size()-20) continue; // invalid text
|
|
// parse text
|
|
int ipos = dataOfs;
|
|
if (baGetDW(ba, ipos) != 20) continue; // invalid header size
|
|
if (baGetDW(ba, ipos) != 0xfffd0001) continue; // invalid type
|
|
quint32 subT = baGetDW(ba, ipos);
|
|
if (baGetDW(ba, ipos) != 1) continue; // invalid version
|
|
quint32 len = baGetDW(ba, ipos);
|
|
// check for data presence
|
|
if (ipos+len > (quint32)ba.size()) continue; // out of data
|
|
QByteArray stBA(ba.mid(ipos, len));
|
|
QString s(QString::fromUtf8(stBA));
|
|
switch (subT) {
|
|
case 1: // copyright (author)
|
|
if (!wasAuthor) {
|
|
wasAuthor = true;
|
|
mAuthor = s;
|
|
}
|
|
break;
|
|
case 2: // license
|
|
if (!wasLic) {
|
|
wasLic = true;
|
|
mLicense = s;
|
|
}
|
|
break;
|
|
case 3: // other (description)
|
|
if (!wasDescr) {
|
|
wasDescr = true;
|
|
mDescr = s;
|
|
}
|
|
break;
|
|
// my extensions
|
|
case 4: // title
|
|
if (!wasTitle) {
|
|
wasTitle = true;
|
|
mTitle = s;
|
|
}
|
|
break;
|
|
case 5: // e-mail
|
|
if (!wasMail) {
|
|
wasMail = true;
|
|
mEMail = s;
|
|
}
|
|
break;
|
|
case 6: // site
|
|
if (!wasSite) {
|
|
wasSite = true;
|
|
mSite = s;
|
|
}
|
|
break;
|
|
case 7: // IM
|
|
if (!wasIM) {
|
|
wasIM = true;
|
|
mIM = s;
|
|
}
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
if (type != 0xfffd0002) continue; // not an image, skip this one
|
|
// image
|
|
// check
|
|
if (dataOfs < tocEndOfs || dataOfs > (quint32)ba.size()-36) continue; // invalid image
|
|
// parse image
|
|
int ipos = dataOfs;
|
|
if (baGetDW(ba, ipos) != 36) continue; // invalid image (header size)
|
|
if (baGetDW(ba, ipos) != 0xfffd0002) continue; // invalid type
|
|
ipos += 4; // skip the shit
|
|
if (baGetDW(ba, ipos) != 1) continue; // invalid image (version)
|
|
quint32 wdt = baGetDW(ba, ipos);
|
|
quint32 hgt = baGetDW(ba, ipos);
|
|
if (wdt > 0x7fff || hgt > 0x7fff) continue; // invalid sizes
|
|
// check for data presence
|
|
if ((ipos+3*4)+(wdt*hgt*4) > (quint32)ba.size()) continue; // out of data
|
|
// load image
|
|
const uchar *dta = (const uchar *)ba.constData();
|
|
dta += dataOfs;
|
|
XCursorImage *img = new XCursorImageXCur(mName+"_"+QString::number(cnt), dta);
|
|
if (img->isValid()) mList << img; else delete img;
|
|
}
|
|
return true;
|
|
}
|