summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--tdecore/CMakeLists.txt2
-rw-r--r--tdecore/hwlibdata/CMakeLists.txt1
-rw-r--r--tdecore/hwlibdata/pnpdev/CMakeLists.txt17
-rw-r--r--tdecore/hwlibdata/pnpdev/pnp.ids156
-rw-r--r--tdecore/ksimpledirwatch.cpp1755
-rw-r--r--tdecore/ksimpledirwatch.h293
-rw-r--r--tdecore/ksimpledirwatch_p.h161
-rw-r--r--tdecore/tdehardwaredevices.cpp683
-rw-r--r--tdecore/tdehardwaredevices.h189
9 files changed, 3225 insertions, 32 deletions
diff --git a/tdecore/CMakeLists.txt b/tdecore/CMakeLists.txt
index 422a1f431..f96c212d6 100644
--- a/tdecore/CMakeLists.txt
+++ b/tdecore/CMakeLists.txt
@@ -123,7 +123,7 @@ set( ${target}_SRCS
ktempdir.cpp kshell.cpp kmountpoint.cpp kcalendarsystemjalali.cpp
kprotocolinfo_tdecore.cpp kprotocolinfofactory.cpp kxerrorhandler.cpp
kuser.cpp kconfigskeleton.cpp kconfigdialogmanager.cpp klockfile.cpp
- kqiodevicegzip_p.cpp ktimezones.cpp tdehardwaredevices.cpp
+ kqiodevicegzip_p.cpp ktimezones.cpp tdehardwaredevices.cpp ksimpledirwatch.cpp
)
tde_add_library( ${target} SHARED AUTOMOC
diff --git a/tdecore/hwlibdata/CMakeLists.txt b/tdecore/hwlibdata/CMakeLists.txt
index d18877c5f..fe7cee67d 100644
--- a/tdecore/hwlibdata/CMakeLists.txt
+++ b/tdecore/hwlibdata/CMakeLists.txt
@@ -11,3 +11,4 @@
add_subdirectory( classrules )
+add_subdirectory( pnpdev )
diff --git a/tdecore/hwlibdata/pnpdev/CMakeLists.txt b/tdecore/hwlibdata/pnpdev/CMakeLists.txt
new file mode 100644
index 000000000..61b1df38e
--- /dev/null
+++ b/tdecore/hwlibdata/pnpdev/CMakeLists.txt
@@ -0,0 +1,17 @@
+#################################################
+#
+# (C) 2012 Timothy Pearson
+# kb9vqf (AT) pearsoncomputing.net
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+
+##### other data ################################
+
+install( FILES
+ pnp.ids
+ DESTINATION ${DATA_INSTALL_DIR}/tdehwlib/pnpdev )
diff --git a/tdecore/hwlibdata/pnpdev/pnp.ids b/tdecore/hwlibdata/pnpdev/pnp.ids
new file mode 100644
index 000000000..bf4a3ce01
--- /dev/null
+++ b/tdecore/hwlibdata/pnpdev/pnp.ids
@@ -0,0 +1,156 @@
+# The information provided in this PNP ID to friendly name mapping table was obtained from
+# multiple public sources, as well as this authoritative publicly accessible file:
+# ftp://ftpmicrosoftcom/developr/drg/plug-and-play/devidstxt
+#
+# This file is provided soley in the interest of cross platform compatibility
+# It only contains information on certain generic core system devices that cannot be looked up via another method
+# All marks are the property of their owners
+#
+# A typical PNP ID takes the form of PNPxxxx:yy
+#
+# Some additional information that may be useful for applications parsing PNP device ID strings:
+# PNP0xxx System devices
+# PNP8xxx Network adapters
+# PNPAxxx SCSI, proprietary CD adapters
+# PNPBxxx Sound, video capture, multimedia
+# PNPCxxx - PNPDxxx Modems
+#
+# The two digit code yy appears to be a PCI device type code followed by a subtype code and should be parsed as such
+
+PNP0802 Microsoft® Sound System-compatible device
+
+PNP0000 AT Interrupt Controller
+PNP0001 EISA Interrupt Controller
+PNP0002 MCA Interrupt Controller
+PNP0003 APIC
+PNP0004 Cyrix SLiC MP Interrupt Controller
+
+PNP0100 AT Timer
+PNP0101 EISA Timer
+PNP0102 MCA Timer
+PNP0103 High Precision Event Timer
+
+PNP0200 AT DMA Controller
+PNP0201 EISA DMA Controller
+PNP0202 MCA DMA Controller
+
+PNP0300 IBM PC/XT keyboard controller (83-key)
+PNP0301 IBM PC/AT keyboard controller (86-key)
+PNP0302 IBM PC/XT keyboard controller (84-key)
+PNP0303 IBM Enhanced (101/102-key, PS/2 mouse support)
+PNP0304 Olivetti Keyboard (83-key)
+PNP0305 Olivetti Keyboard (102-key)
+PNP0306 Olivetti Keyboard (86-key)
+PNP0307 Microsoft® Windows(R) Keyboard
+PNP0308 General Input Device Emulation Interface (GIDEI) legacy
+PNP0309 Olivetti Keyboard (A101/102 key)
+PNP030A AT&T 302 keyboard
+PNP030B Reserved by Microsoft®
+PNP0320 Japanese 106-key keyboard A01
+PNP0321 Japanese 101-key keyboard
+PNP0322 Japanese AX keyboard
+PNP0323 Japanese 106-key keyboard 002/003
+PNP0324 Japanese 106-key keyboard 001
+PNP0325 Japanese Toshiba Desktop keyboard
+PNP0326 Japanese Toshiba Laptop keyboard
+PNP0327 Japanese Toshiba Notebook keyboard
+PNP0340 Korean 84-key keyboard
+PNP0341 Korean 86-key keyboard
+PNP0342 Korean Enhanced keyboard
+PNP0343 Korean Enhanced keyboard 101b
+PNP0343 Korean Enhanced keyboard 101c
+PNP0344 Korean Enhanced keyboard 103
+
+PNP0400 Standard LPT Printer Port
+PNP0401 ECP Printer Port
+
+PNP0500 Standard PC Serial Port
+PNP0501 16550A-compatible Serial Port
+PNP0502 Multiport Serial Device (non-intelligent 16550)
+PNP0510 Generic IRDA-compatible Device
+PNP0511 Generic IRDA-compatible Device
+
+PNP0600 Generic ESDI/IDE/ATA Compatible Hard Disk Controller
+PNP0603 Generic IDE supporting Microsoft® Device Bay Specification
+PNP0700 PC Standard Floppy Disk Controller
+PNP0701 Microsoft® Device Bay Compatible Floppy Controller
+
+PNP0802 Microsoft® Sound System compatible device
+
+PNP0900 VGA Compatible Display Controller
+PNP09FF Plug and Play Monitor (VESA DDC)
+
+PNP0A00 ISA Root Bus
+PNP0A01 EISA Root Bus
+PNP0A02 MCA Root Bus
+PNP0A03 PCI Root Bus
+PNP0A04 VESA/VL Bus
+PNP0A05 Generic ACPI Root Bus
+PNP0A06 Generic ACPI Extended-IO Root Bus (EIO Root Bus)
+PNP0A08 PCI Express Root Bus
+
+PNP0800 AT-style Speaker
+PNP0B00 AT Real-Time Clock
+PNP0C00 Plug and Play BIOS
+PNP0C01 System Board
+PNP0C02 Plug and Play Motherboard Register Resource
+PNP0C03 Plug and Play BIOS Event Notification Interrupt
+PNP0C04 Math Coprocessor
+PNP0C05 APM BIOS
+PNP0C06 Early Plug and Play BIOS
+PNP0C07 Early Plug and Play BIOS
+PNP0C08 ACPI System Board
+PNP0C09 ACPI Embedded Controller
+PNP0C0A ACPI Control Method Battery
+PNP0C0B ACPI Fan
+PNP0C0C ACPI Power Button
+PNP0C0D ACPI Lid Switch
+PNP0C0E ACPI Sleep Button
+PNP0C0F PCI Interrupt Link
+PNP0C10 ACPI System Indicator
+PNP0C11 ACPI Thermal Zone
+PNP0C12 Device Bay Controller
+PNP0C13 Plug and Play BIOS
+
+PNP0E00 Intel 82365-Compatible PCMCIA Controller
+PNP0E01 Cirrus Logic CL-PD6720 PCMCIA Controller
+PNP0E02 VLSI VL82C146 PCMCIA Controller
+PNP0E03 Intel 82365-compatible CardBus controller
+
+PNP0F00 Microsoft® Bus Mouse
+PNP0F01 Microsoft® Serial Mouse
+PNP0F02 Microsoft® InPort Mouse
+PNP0F03 Microsoft® PS/2-style Mouse
+PNP0F04 Mouse Systems Mouse
+PNP0F05 Mouse Systems 3-Button Mouse
+PNP0F06 Genius Mouse
+PNP0F07 Genius Mouse
+PNP0F08 Logitech Serial Mouse
+PNP0F09 Microsoft® BallPoint Serial Mouse
+PNP0F0A Microsoft® Plug and Play Mouse
+PNP0F0B Microsoft® Plug and Play BallPoint Mouse
+PNP0F0C Microsoft®-compatible Serial Mouse
+PNP0F0D Microsoft®-compatible InPort-compatible Mouse
+PNP0F0E Microsoft®-compatible PS/2-style Mouse
+PNP0F0F Microsoft®-compatible Serial BallPoint-compatible Mouse
+PNP0F10 Texas Instruments QuickPort Mouse
+PNP0F11 Microsoft®-compatible Bus Mouse
+PNP0F12 Logitech PS/2-style Mouse
+PNP0F13 PS/2 Port for PS/2-style Mice
+PNP0F14 Microsoft® Kids Mouse
+PNP0F15 Logitech bus mouse
+PNP0F16 Logitech SWIFT device
+PNP0F17 Logitech-compatible serial mouse
+PNP0F18 Logitech-compatible bus mouse
+PNP0F19 Logitech-compatible PS/2-style Mouse
+PNP0F1A Logitech-compatible SWIFT Device
+PNP0F1B HP Omnibook Mouse
+PNP0F1C Compaq LTE Trackball PS/2-style Mouse
+PNP0F1D Compaq LTE Trackball Serial Mouse
+PNP0F1E Microsoft® Kids Trackball Mouse
+PNP0F1F Reserved by Microsoft® Input Device Group
+PNP0F20 Reserved by Microsoft® Input Device Group
+PNP0F21 Reserved by Microsoft® Input Device Group
+PNP0F22 Reserved by Microsoft® Input Device Group
+PNP0F23 Reserved by Microsoft® Input Device Group
+PNP0FFF Reserved by Microsoft® Systems \ No newline at end of file
diff --git a/tdecore/ksimpledirwatch.cpp b/tdecore/ksimpledirwatch.cpp
new file mode 100644
index 000000000..cad0345ab
--- /dev/null
+++ b/tdecore/ksimpledirwatch.cpp
@@ -0,0 +1,1755 @@
+// -*- c-basic-offset: 2 -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 1998 Sven Radej <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+// KSimpleDirWatch is a basic copy of KDirWatch
+// but with the KIO linking requirement removed
+
+#include <config.h>
+#include <errno.h>
+
+#ifdef HAVE_DNOTIFY
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#endif
+
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqintdict.h>
+#include <tqptrlist.h>
+#include <tqsocketnotifier.h>
+#include <tqstringlist.h>
+#include <tqtimer.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kstaticdeleter.h>
+#include <kde_file.h>
+
+// debug
+#include <sys/ioctl.h>
+
+#ifdef HAVE_INOTIFY
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/syscall.h>
+#include <linux/types.h>
+// Linux kernel headers are documented to not compile
+#define _S390_BITOPS_H
+#include <sys/inotify.h>
+
+#ifndef __NR_inotify_init
+#if defined(__i386__)
+#define __NR_inotify_init 291
+#define __NR_inotify_add_watch 292
+#define __NR_inotify_rm_watch 293
+#endif
+#if defined(__PPC__)
+#define __NR_inotify_init 275
+#define __NR_inotify_add_watch 276
+#define __NR_inotify_rm_watch 277
+#endif
+#if defined(__x86_64__)
+#define __NR_inotify_init 253
+#define __NR_inotify_add_watch 254
+#define __NR_inotify_rm_watch 255
+#endif
+#endif
+
+#ifndef IN_ONLYDIR
+#define IN_ONLYDIR 0x01000000
+#endif
+
+#ifndef IN_DONT_FOLLOW
+#define IN_DONT_FOLLOW 0x02000000
+#endif
+
+#ifndef IN_MOVE_SELF
+#define IN_MOVE_SELF 0x00000800
+#endif
+
+#endif
+
+#include <sys/utsname.h>
+
+#include "ksimpledirwatch.h"
+#include "ksimpledirwatch_p.h"
+
+#define NO_NOTIFY (time_t) 0
+
+static KSimpleDirWatchPrivate* dwp_self = 0;
+
+#ifdef HAVE_DNOTIFY
+
+static int dnotify_signal = 0;
+
+/* DNOTIFY signal handler
+ *
+ * As this is called asynchronously, only a flag is set and
+ * a rescan is requested.
+ * This is done by writing into a pipe to trigger a TQSocketNotifier
+ * watching on this pipe: a timer is started and after a timeout,
+ * the rescan is done.
+ */
+void KSimpleDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
+{
+ if (!dwp_self) return;
+
+ // write might change errno, we have to save it and restore it
+ // (Richard Stevens, Advanced programming in the Unix Environment)
+ int saved_errno = errno;
+
+ Entry* e = dwp_self->fd_Entry.find(si->si_fd);
+
+// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
+// << TQString(e ? e->path:"unknown") << endl;
+
+ if(e && e->dn_fd == si->si_fd)
+ e->dirty = true;
+
+ char c = 0;
+ write(dwp_self->mPipe[1], &c, 1);
+ errno = saved_errno;
+}
+
+static struct sigaction old_sigio_act;
+/* DNOTIFY SIGIO signal handler
+ *
+ * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
+ */
+void KSimpleDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
+{
+ if (dwp_self)
+ {
+ // write might change errno, we have to save it and restore it
+ // (Richard Stevens, Advanced programming in the Unix Environment)
+ int saved_errno = errno;
+
+ dwp_self->rescan_all = true;
+ char c = 0;
+ write(dwp_self->mPipe[1], &c, 1);
+
+ errno = saved_errno;
+ }
+
+ // Call previous signal handler
+ if (old_sigio_act.sa_flags & SA_SIGINFO)
+ {
+ if (old_sigio_act.sa_sigaction)
+ (*old_sigio_act.sa_sigaction)(sig, si, p);
+ }
+ else
+ {
+ if ((old_sigio_act.sa_handler != SIG_DFL) &&
+ (old_sigio_act.sa_handler != SIG_IGN))
+ (*old_sigio_act.sa_handler)(sig);
+ }
+}
+#endif
+
+
+//
+// Class KSimpleDirWatchPrivate (singleton)
+//
+
+/* All entries (files/directories) to be watched in the
+ * application (coming from multiple KSimpleDirWatch instances)
+ * are registered in a single KSimpleDirWatchPrivate instance.
+ *
+ * At the moment, the following methods for file watching
+ * are supported:
+ * - Polling: All files to be watched are polled regularly
+ * using stat (more precise: TQFileInfo.lastModified()).
+ * The polling frequency is determined from global kconfig
+ * settings, defaulting to 500 ms for local directories
+ * and 5000 ms for remote mounts
+ * - FAM (File Alternation Monitor): first used on IRIX, SGI
+ * has ported this method to LINUX. It uses a kernel part
+ * (IMON, sending change events to /dev/imon) and a user
+ * level damon (fam), to which applications connect for
+ * notification of file changes. For NFS, the fam damon
+ * on the NFS server machine is used; if IMON is not built
+ * into the kernel, fam uses polling for local files.
+ * - DNOTIFY: In late LINUX 2.3.x, directory notification was
+ * introduced. By opening a directory, you can request for
+ * UNIX signals to be sent to the process when a directory
+ * is changed.
+ * - INOTIFY: In LINUX 2.6.13, inode change notification was
+ * introduced. You're now able to watch arbitrary inode's
+ * for changes, and even get notification when they're
+ * unmounted.
+ */
+
+KSimpleDirWatchPrivate::KSimpleDirWatchPrivate()
+ : rescan_timer(0, "KSimpleDirWatchPrivate::rescan_timer")
+{
+ timer = new TQTimer(this, "KSimpleDirWatchPrivate::timer");
+ connect (timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
+ freq = 3600000; // 1 hour as upper bound
+ statEntries = 0;
+ delayRemove = false;
+ m_ref = 0;
+
+ KConfigGroup config(KGlobal::config(), TQCString("DirWatch"));
+ m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
+ m_PollInterval = config.readNumEntry("PollInterval", 500);
+
+ TQString available("Stat");
+
+ // used for FAM and DNOTIFY
+ rescan_all = false;
+ connect(&rescan_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
+
+#ifdef HAVE_FAM
+ // It's possible that FAM server can't be started
+ if (FAMOpen(&fc) ==0) {
+ available += ", FAM";
+ use_fam=true;
+ sn = new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
+ TQSocketNotifier::Read, this);
+ connect( sn, TQT_SIGNAL(activated(int)),
+ this, TQT_SLOT(famEventReceived()) );
+ }
+ else {
+ kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
+ use_fam=false;
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ supports_inotify = true;
+
+ m_inotify_fd = inotify_init();
+
+ if ( m_inotify_fd <= 0 ) {
+ kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl;
+ supports_inotify = false;
+ }
+
+ {
+ struct utsname uts;
+ int major, minor, patch;
+ if (uname(&uts) < 0)
+ supports_inotify = false; // *shrug*
+ else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+ supports_inotify = false; // *shrug*
+ else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
+ kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl;
+ supports_inotify = false;
+ }
+ }
+
+ if ( supports_inotify ) {
+ available += ", Inotify";
+ fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
+
+ mSn = new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read, this );
+ connect( mSn, TQT_SIGNAL(activated( int )), this, TQT_SLOT( slotActivated() ) );
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+
+ // if we have inotify, disable dnotify.
+#ifdef HAVE_INOTIFY
+ supports_dnotify = !supports_inotify;
+#else
+ // otherwise, not guilty until proven guilty.
+ supports_dnotify = true;
+#endif
+
+ struct utsname uts;
+ int major, minor, patch;
+ if (uname(&uts) < 0)
+ supports_dnotify = false; // *shrug*
+ else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+ supports_dnotify = false; // *shrug*
+ else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
+ kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
+ supports_dnotify = false;
+ }
+
+ if( supports_dnotify ) {
+ available += ", DNotify";
+
+ pipe(mPipe);
+ fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
+ fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
+ fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
+ mSn = new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read, this);
+ connect(mSn, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated()));
+ // Install the signal handler only once
+ if ( dnotify_signal == 0 )
+ {
+ dnotify_signal = SIGRTMIN + 8;
+
+ struct sigaction act;
+ act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+ sigaction(dnotify_signal, &act, NULL);
+
+ act.sa_sigaction = KSimpleDirWatchPrivate::dnotify_sigio_handler;
+ sigaction(SIGIO, &act, &old_sigio_act);
+ }
+ }
+ else
+ {
+ mPipe[0] = -1;
+ mPipe[1] = -1;
+ }
+#endif
+
+ kdDebug(7001) << "Available methods: " << available << endl;
+}
+
+/* This is called on app exit (KStaticDeleter) */
+KSimpleDirWatchPrivate::~KSimpleDirWatchPrivate()
+{
+ timer->stop();
+
+ /* remove all entries being watched */
+ removeEntries(0);
+
+#ifdef HAVE_FAM
+ if (use_fam) {
+ FAMClose(&fc);
+ kdDebug(7001) << "KSimpleDirWatch deleted (FAM closed)" << endl;
+ }
+#endif
+#ifdef HAVE_INOTIFY
+ if ( supports_inotify )
+ ::close( m_inotify_fd );
+#endif
+#ifdef HAVE_DNOTIFY
+ close(mPipe[0]);
+ close(mPipe[1]);
+#endif
+}
+
+#include <stdlib.h>
+
+void KSimpleDirWatchPrivate::slotActivated()
+{
+#ifdef HAVE_DNOTIFY
+ if ( supports_dnotify )
+ {
+ char dummy_buf[4096];
+ read(mPipe[0], &dummy_buf, 4096);
+
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true /* singleshot */);
+
+ return;
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ if ( !supports_inotify )
+ return;
+
+ int pending = -1;
+ int offset = 0;
+ char buf[4096];
+ assert( m_inotify_fd > -1 );
+ ioctl( m_inotify_fd, FIONREAD, &pending );
+
+ while ( pending > 0 ) {
+
+ if ( pending > (int)sizeof( buf ) )
+ pending = sizeof( buf );
+
+ pending = read( m_inotify_fd, buf, pending);
+
+ while ( pending > 0 ) {
+ struct inotify_event *event = (struct inotify_event *) &buf[offset];
+ pending -= sizeof( struct inotify_event ) + event->len;
+ offset += sizeof( struct inotify_event ) + event->len;
+
+ TQString path;
+ if ( event->len )
+ path = TQFile::decodeName( TQCString( event->name, event->len ) );
+
+ if ( path.length() && isNoisyFile( path.latin1() ) )
+ continue;
+
+ kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl;
+
+ // now we're in deep trouble of finding the
+ // associated entries
+ // for now, we suck and iterate
+ for ( EntryMap::Iterator it = m_mapEntries.begin();
+ it != m_mapEntries.end(); ++it ) {
+ Entry* e = &( *it );
+ if ( e->wd == event->wd ) {
+ e->dirty = true;
+
+ if ( 1 || e->isDir) {
+ if( event->mask & IN_DELETE_SELF) {
+ kdDebug(7001) << "-->got deleteself signal for " << e->path << endl;
+ e->m_status = NonExistent;
+ if (e->isDir)
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ else
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+ if ( event->mask & IN_IGNORED ) {
+ e->wd = 0;
+ }
+ if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
+ Entry *sub_entry = e->m_entries.first();
+ for(;sub_entry; sub_entry = e->m_entries.next())
+ if (sub_entry->path == e->path + "/" + path) break;
+
+ if (sub_entry /*&& sub_entry->isDir*/) {
+ removeEntry(0,e->path, sub_entry);
+ KDE_struct_stat stat_buf;
+ TQCString tpath = TQFile::encodeName(path);
+ KDE_stat(tpath, &stat_buf);
+
+ //sub_entry->isDir = S_ISDIR(stat_buf.st_mode);
+ //sub_entry->m_ctime = stat_buf.st_ctime;
+ //sub_entry->m_status = Normal;
+ //sub_entry->m_nlink = stat_buf.st_nlink;
+
+ if(!useINotify(sub_entry))
+ useStat(sub_entry);
+ sub_entry->dirty = true;
+ }
+ }
+ }
+
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true /* singleshot */);
+
+ break; // there really should be only one matching wd
+ }
+ }
+
+ }
+ }
+#endif
+}
+
+/* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned.
+ * We first need to mark all yet nonexistent, but possible created
+ * entries as dirty...
+ */
+void KSimpleDirWatchPrivate::Entry::propagate_dirty()
+{
+ for (TQPtrListIterator<Entry> sub_entry (m_entries);
+ sub_entry.current(); ++sub_entry)
+ {
+ if (!sub_entry.current()->dirty)
+ {
+ sub_entry.current()->dirty = true;
+ sub_entry.current()->propagate_dirty();
+ }
+ }
+}
+
+
+/* A KSimpleDirWatch instance is interested in getting events for
+ * this file/Dir entry.
+ */
+void KSimpleDirWatchPrivate::Entry::addClient(KSimpleDirWatch* instance)
+{
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ if (client->instance == instance) break;
+
+ if (client) {
+ client->count++;
+ return;
+ }
+
+ client = new Client;
+ client->instance = instance;
+ client->count = 1;
+ client->watchingStopped = instance->isStopped();
+ client->pending = NoChange;
+
+ m_clients.append(client);
+}
+
+void KSimpleDirWatchPrivate::Entry::removeClient(KSimpleDirWatch* instance)
+{
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ if (client->instance == instance) break;
+
+ if (client) {
+ client->count--;
+ if (client->count == 0) {
+ m_clients.removeRef(client);
+ delete client;
+ }
+ }
+}
+
+/* get number of clients */
+int KSimpleDirWatchPrivate::Entry::clients()
+{
+ int clients = 0;
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ clients += client->count;
+
+ return clients;
+}
+
+
+KSimpleDirWatchPrivate::Entry* KSimpleDirWatchPrivate::entry(const TQString& _path)
+{
+// we only support absolute paths
+ if (TQDir::isRelativePath(_path)) {
+ return 0;
+ }
+
+ TQString path = _path;
+
+ if ( path.length() > 1 && path.right(1) == "/" )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it == m_mapEntries.end() )
+ return 0;
+ else
+ return &(*it);
+}
+
+// set polling frequency for a entry and adjust global freq if needed
+void KSimpleDirWatchPrivate::useFreq(Entry* e, int newFreq)
+{
+ e->freq = newFreq;
+
+ // a reasonable frequency for the global polling timer
+ if (e->freq < freq) {
+ freq = e->freq;
+ if (timer->isActive()) timer->changeInterval(freq);
+ kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
+ }
+}
+
+
+#ifdef HAVE_FAM
+// setup FAM notification, returns false if not possible
+bool KSimpleDirWatchPrivate::useFAM(Entry* e)
+{
+ if (!use_fam) return false;
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ e->m_mode = FAMMode;
+ e->dirty = false;
+
+ if (e->isDir) {
+ if (e->m_status == NonExistent) {
+ // If the directory does not exist we watch the parent directory
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ }
+ else {
+ int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ return false;
+ }
+ kdDebug(7001) << " Setup FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ }
+ else {
+ if (e->m_status == NonExistent) {
+ // If the file does not exist we watch the directory
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+ else {
+ int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ return false;
+ }
+
+ kdDebug(7001) << " Setup FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ }
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ return true;
+}
+#endif
+
+
+#ifdef HAVE_DNOTIFY
+// setup DNotify notification, returns false if not possible
+bool KSimpleDirWatchPrivate::useDNotify(Entry* e)
+{
+ e->dn_fd = 0;
+ e->dirty = false;
+ if (!supports_dnotify) return false;
+
+ e->m_mode = DNotifyMode;
+
+ if (e->isDir) {
+ if (e->m_status == Normal) {
+ int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
+ // Migrate fd to somewhere above 128. Some libraries have
+ // constructs like:
+ // fd = socket(...)
+ // if (fd > ARBITRARY_LIMIT)
+ // return error;
+ //
+ // Since programs might end up using a lot of KSimpleDirWatch objects
+ // for a rather long time the above braindamage could get
+ // triggered.
+ //
+ // By moving the ksimpledirwatch fd's to > 128, calls like socket() will keep
+ // returning fd's < ARBITRARY_LIMIT for a bit longer.
+ int fd2 = fcntl(fd, F_DUPFD, 128);
+ if (fd2 >= 0)
+ {
+ close(fd);
+ fd = fd2;
+ }
+ if (fd<0) {
+ e->m_mode = UnknownMode;
+ return false;
+ }
+
+ int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
+ if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
+
+ if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
+ fcntl(fd, F_NOTIFY, mask) < 0) {
+
+ kdDebug(7001) << "Not using Linux Directory Notifications."
+ << endl;
+ supports_dnotify = false;
+ ::close(fd);
+ e->m_mode = UnknownMode;
+ return false;
+ }
+
+ fd_Entry.replace(fd, e);
+ e->dn_fd = fd;
+
+ kdDebug(7001) << " Setup DNotify (fd " << fd
+ << ") for " << e->path << endl;
+ }
+ else { // NotExisting
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ }
+ }
+ else { // File
+ // we always watch the directory (DNOTIFY can't watch files alone)
+ // this notifies us about changes of files therein
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+
+ return true;
+}
+#endif
+
+#ifdef HAVE_INOTIFY
+// setup INotify notification, returns false if not possible
+bool KSimpleDirWatchPrivate::useINotify( Entry* e )
+{
+ e->wd = 0;
+ e->dirty = false;
+ if (!supports_inotify) return false;
+
+ e->m_mode = INotifyMode;
+
+ int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
+ if(!e->isDir)
+ mask |= IN_MODIFY|IN_ATTRIB;
+ else
+ mask |= IN_ONLYDIR;
+
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
+ if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
+ }
+
+ if ( ( e->wd = inotify_add_watch( m_inotify_fd,
+ TQFile::encodeName( e->path ), mask) ) > 0 )
+ return true;
+
+ if ( e->m_status == NonExistent ) {
+ if (e->isDir)
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ else
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+bool KSimpleDirWatchPrivate::useStat(Entry* e)
+{
+ useFreq(e, m_PollInterval);
+
+ if (e->m_mode != StatMode) {
+ e->m_mode = StatMode;
+ statEntries++;
+
+ if ( statEntries == 1 ) {
+ // if this was first STAT entry (=timer was stopped)
+ timer->start(freq); // then start the timer
+ kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
+ }
+ }
+
+ kdDebug(7001) << " Setup Stat (freq " << e->freq
+ << ") for " << e->path << endl;
+
+ return true;
+}
+
+
+/* If <instance> !=0, this KSimpleDirWatch instance wants to watch at <_path>,
+ * providing in <isDir> the type of the entry to be watched.
+ * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
+ * this entry needs another entry to watch himself (when notExistent).
+ */
+void KSimpleDirWatchPrivate::addEntry(KSimpleDirWatch* instance, const TQString& _path,
+ Entry* sub_entry, bool isDir)
+{
+ TQString path = _path;
+ if (path.startsWith("/dev/") || (path == "/dev"))
+ return; // Don't even go there.
+
+ if ( path.length() > 1 && path.right(1) == "/" )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it != m_mapEntries.end() )
+ {
+ if (sub_entry) {
+ (*it).m_entries.append(sub_entry);
+ kdDebug(7001) << "Added already watched Entry " << path
+ << " (for " << sub_entry->path << ")" << endl;
+
+#ifdef HAVE_DNOTIFY
+ {
+ Entry* e = &(*it);
+ if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
+ int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
+ if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
+ if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
+ ::close(e->dn_fd);
+ e->m_mode = UnknownMode;
+ fd_Entry.remove(e->dn_fd);
+ e->dn_fd = 0;
+ useStat( e );
+ }
+ }
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ {
+ Entry* e = &(*it);
+ if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
+ int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
+ if(!e->isDir)
+ mask |= IN_MODIFY|IN_ATTRIB;
+ else
+ mask |= IN_ONLYDIR;
+
+ inotify_rm_watch (m_inotify_fd, e->wd);
+ e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
+ }
+ }
+#endif
+
+ }
+ else {
+ (*it).addClient(instance);
+ kdDebug(7001) << "Added already watched Entry " << path
+ << " (now " << (*it).clients() << " clients)"
+ << TQString(TQString(" [%1]").arg(instance->name())) << endl;
+ }
+ return;
+ }
+
+ // we have a new path to watch
+
+ KDE_struct_stat stat_buf;
+ TQCString tpath = TQFile::encodeName(path);
+ bool exists = (KDE_stat(tpath, &stat_buf) == 0);
+
+ Entry newEntry;
+ m_mapEntries.insert( path, newEntry );
+ // the insert does a copy, so we have to use <e> now
+ Entry* e = &(m_mapEntries[path]);
+
+ if (exists) {
+ e->isDir = S_ISDIR(stat_buf.st_mode);
+
+ if (e->isDir && !isDir)
+ kdWarning() << "KSimpleDirWatch: " << path << " is a directory. Use addDir!" << endl;
+ else if (!e->isDir && isDir)
+ kdWarning() << "KSimpleDirWatch: " << path << " is a file. Use addFile!" << endl;
+
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ }
+ else {
+ e->isDir = isDir;
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ }
+
+ e->path = path;
+ if (sub_entry)
+ e->m_entries.append(sub_entry);
+ else
+ e->addClient(instance);
+
+ kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
+ << (e->m_status == NonExistent ? " NotExisting" : "")
+ << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
+ << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
+ << endl;
+
+
+ // now setup the notification method
+ e->m_mode = UnknownMode;
+ e->msecLeft = 0;
+
+ if ( isNoisyFile( tpath ) )
+ return;
+
+#ifdef HAVE_FAM
+ if (useFAM(e)) return;
+#endif
+
+#ifdef HAVE_INOTIFY
+ if (useINotify(e)) return;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if (useDNotify(e)) return;
+#endif
+
+ useStat(e);
+}
+
+
+void KSimpleDirWatchPrivate::removeEntry( KSimpleDirWatch* instance,
+ const TQString& _path, Entry* sub_entry )
+{
+ kdDebug(7001) << "KSimpleDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl;
+ Entry* e = entry(_path);
+ if (!e) {
+ kdDebug(7001) << "KSimpleDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl;
+ return;
+ }
+
+ if (sub_entry)
+ e->m_entries.removeRef(sub_entry);
+ else
+ e->removeClient(instance);
+
+ if (e->m_clients.count() || e->m_entries.count()) {
+ kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl;
+ return;
+ }
+
+ if (delayRemove) {
+ // removeList is allowed to contain any entry at most once
+ if (removeList.findRef(e)==-1)
+ removeList.append(e);
+ // now e->isValid() is false
+ return;
+ }
+
+#ifdef HAVE_FAM
+ if (e->m_mode == FAMMode) {
+ if ( e->m_status == Normal) {
+ FAMCancelMonitor(&fc, &(e->fr) );
+ kdDebug(7001) << "Cancelled FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ else {
+ if (e->isDir)
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ else
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl;
+ if (e->m_mode == INotifyMode) {
+ if ( e->m_status == Normal ) {
+ (void) inotify_rm_watch( m_inotify_fd, e->wd );
+ kdDebug(7001) << "Cancelled INotify (fd " <<
+ m_inotify_fd << ", " << e->wd <<
+ ") for " << e->path << endl;
+ }
+ else {
+ if (e->isDir)
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ else
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if (e->m_mode == DNotifyMode) {
+ if (!e->isDir) {
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ else { // isDir
+ // must close the FD.
+ if ( e->m_status == Normal) {
+ if (e->dn_fd) {
+ ::close(e->dn_fd);
+ fd_Entry.remove(e->dn_fd);
+
+ kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
+ << ") for " << e->path << endl;
+ e->dn_fd = 0;
+
+ }
+ }
+ else {
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ }
+ }
+ }
+#endif
+
+ if (e->m_mode == StatMode) {
+ statEntries--;
+ if ( statEntries == 0 ) {
+ timer->stop(); // stop timer if lists are empty
+ kdDebug(7001) << " Stopped Polling Timer" << endl;
+ }
+ }
+
+ kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
+ << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
+ << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
+ << endl;
+ m_mapEntries.remove( e->path ); // <e> not valid any more
+}
+
+
+/* Called from KSimpleDirWatch destructor:
+ * remove <instance> as client from all entries
+ */
+void KSimpleDirWatchPrivate::removeEntries( KSimpleDirWatch* instance )
+{
+ TQPtrList<Entry> list;
+ int minfreq = 3600000;
+
+ // put all entries where instance is a client in list
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Client* c = (*it).m_clients.first();
+ for(;c;c=(*it).m_clients.next())
+ if (c->instance == instance) break;
+ if (c) {
+ c->count = 1; // forces deletion of instance as client
+ list.append(&(*it));
+ }
+ else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
+ minfreq = (*it).freq;
+ }
+
+ for(Entry* e=list.first();e;e=list.next())
+ removeEntry(instance, e->path, 0);
+
+ if (minfreq > freq) {
+ // we can decrease the global polling frequency
+ freq = minfreq;
+ if (timer->isActive()) timer->changeInterval(freq);
+ kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
+ }
+}
+
+// instance ==0: stop scanning for all instances
+bool KSimpleDirWatchPrivate::stopEntryScan( KSimpleDirWatch* instance, Entry* e)
+{
+ int stillWatching = 0;
+ Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next()) {
+ if (!instance || instance == c->instance)
+ c->watchingStopped = true;
+ else if (!c->watchingStopped)
+ stillWatching += c->count;
+ }
+
+ kdDebug(7001) << instance->name() << " stopped scanning " << e->path
+ << " (now " << stillWatching << " watchers)" << endl;
+
+ if (stillWatching == 0) {
+ // if nobody is interested, we don't watch
+ e->m_ctime = invalid_ctime; // invalid
+ e->m_status = NonExistent;
+ // e->m_status = Normal;
+ }
+ return true;
+}
+
+// instance ==0: start scanning for all instances
+bool KSimpleDirWatchPrivate::restartEntryScan( KSimpleDirWatch* instance, Entry* e,
+ bool notify)
+{
+ int wasWatching = 0, newWatching = 0;
+ Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next()) {
+ if (!c->watchingStopped)
+ wasWatching += c->count;
+ else if (!instance || instance == c->instance) {
+ c->watchingStopped = false;
+ newWatching += c->count;
+ }
+ }
+ if (newWatching == 0)
+ return false;
+
+ kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path
+ << " (now " << wasWatching+newWatching << " watchers)" << endl;
+
+ // restart watching and emit pending events
+
+ int ev = NoChange;
+ if (wasWatching == 0) {
+ if (!notify) {
+ KDE_struct_stat stat_buf;
+ bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
+ if (exists) {
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ }
+ else {
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ }
+ }
+ e->msecLeft = 0;
+ ev = scanEntry(e);
+ }
+ emitEvent(e,ev);
+
+ return true;
+}
+
+// instance ==0: stop scanning for all instances
+void KSimpleDirWatchPrivate::stopScan(KSimpleDirWatch* instance)
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ stopEntryScan(instance, &(*it));
+}
+
+
+void KSimpleDirWatchPrivate::startScan(KSimpleDirWatch* instance,
+ bool notify, bool skippedToo )
+{
+ if (!notify)
+ resetList(instance,skippedToo);
+
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ restartEntryScan(instance, &(*it), notify);
+
+ // timer should still be running when in polling mode
+}
+
+
+// clear all pending events, also from stopped
+void KSimpleDirWatchPrivate::resetList( KSimpleDirWatch* /*instance*/,
+ bool skippedToo )
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+
+ Client* c = (*it).m_clients.first();
+ for(;c;c=(*it).m_clients.next())
+ if (!c->watchingStopped || skippedToo)
+ c->pending = NoChange;
+ }
+}
+
+// Return event happened on <e>
+//
+int KSimpleDirWatchPrivate::scanEntry(Entry* e)
+{
+#ifdef HAVE_FAM
+ if (e->m_mode == FAMMode) {
+ // we know nothing has changed, no need to stat
+ if(!e->dirty) return NoChange;
+ e->dirty = false;
+ }
+#endif
+
+ // Shouldn't happen: Ignore "unknown" notification method
+ if (e->m_mode == UnknownMode) return NoChange;
+
+#if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
+ if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
+ // we know nothing has changed, no need to stat
+ if(!e->dirty) return NoChange;
+ kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl;
+ e->dirty = false;
+ }
+#endif
+
+ if (e->m_mode == StatMode) {
+ // only scan if timeout on entry timer happens;
+ // e.g. when using 500msec global timer, a entry
+ // with freq=5000 is only watched every 10th time
+
+ e->msecLeft -= freq;
+ if (e->msecLeft>0) return NoChange;
+ e->msecLeft += e->freq;
+ }
+
+ KDE_struct_stat stat_buf;
+ bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
+ if (exists) {
+
+ if (e->m_status == NonExistent) {
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ return Created;
+ }
+
+ if ( (e->m_ctime != invalid_ctime) &&
+ ((stat_buf.st_ctime != e->m_ctime) ||
+ (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_nlink = stat_buf.st_nlink;
+ return Changed;
+ }
+
+ return NoChange;
+ }
+
+ // dir/file doesn't exist
+
+ if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
+ e->m_nlink = 0;
+ e->m_status = NonExistent;
+ return NoChange;
+ }
+
+ e->m_ctime = invalid_ctime;
+ e->m_nlink = 0;
+ e->m_status = NonExistent;
+
+ return Deleted;
+}
+
+/* Notify all interested KSimpleDirWatch instances about a given event on an entry
+ * and stored pending events. When watching is stopped, the event is
+ * added to the pending events.
+ */
+void KSimpleDirWatchPrivate::emitEvent(Entry* e, int event, const TQString &fileName)
+{
+ TQString path = e->path;
+ if (!fileName.isEmpty()) {
+ if (!TQDir::isRelativePath(fileName))
+ path = fileName;
+ else
+#ifdef Q_OS_UNIX
+ path += "/" + fileName;
+#elif defined(Q_WS_WIN)
+ //current drive is passed instead of /
+ path += TQDir::currentDirPath().left(2) + "/" + fileName;
+#endif
+ }
+
+ TQPtrListIterator<Client> cit( e->m_clients );
+ for ( ; cit.current(); ++cit )
+ {
+ Client* c = cit.current();
+
+ if (c->instance==0 || c->count==0) continue;
+
+ if (c->watchingStopped) {
+ // add event to pending...
+ if (event == Changed)
+ c->pending |= event;
+ else if (event == Created || event == Deleted)
+ c->pending = event;
+ continue;
+ }
+ // not stopped
+ if (event == NoChange || event == Changed)
+ event |= c->pending;
+ c->pending = NoChange;
+ if (event == NoChange) continue;
+
+ if (event & Deleted) {
+ c->instance->setDeleted(path);
+ // emit only Deleted event...
+ continue;
+ }
+
+ if (event & Created) {
+ c->instance->setCreated(path);
+ // possible emit Change event after creation
+ }
+
+ if (event & Changed)
+ c->instance->setDirty(path);
+ }
+}
+
+// Remove entries which were marked to be removed
+void KSimpleDirWatchPrivate::slotRemoveDelayed()
+{
+ Entry* e;
+ delayRemove = false;
+ for(e=removeList.first();e;e=removeList.next())
+ removeEntry(0, e->path, 0);
+ removeList.clear();
+}
+
+/* Scan all entries to be watched for changes. This is done regularly
+ * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
+ */
+void KSimpleDirWatchPrivate::slotRescan()
+{
+ EntryMap::Iterator it;
+
+ // People can do very long things in the slot connected to dirty(),
+ // like showing a message box. We don't want to keep polling during
+ // that time, otherwise the value of 'delayRemove' will be reset.
+ bool timerRunning = timer->isActive();
+ if ( timerRunning )
+ timer->stop();
+
+ // We delay deletions of entries this way.
+ // removeDir(), when called in slotDirty(), can cause a crash otherwise
+ delayRemove = true;
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ TQPtrList<Entry> dList, cList;
+#endif
+
+ if (rescan_all)
+ {
+ // mark all as dirty
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ (*it).dirty = true;
+ rescan_all = false;
+ }
+ else
+ {
+ // progate dirty flag to dependant entries (e.g. file watches)
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
+ (*it).propagate_dirty();
+ }
+
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ // we don't check invalid entries (i.e. remove delayed)
+ if (!(*it).isValid()) continue;
+
+ int ev = scanEntry( &(*it) );
+
+
+#ifdef HAVE_INOTIFY
+ if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
+ cList.append( &(*it) );
+ if (! useINotify( &(*it) )) {
+ useStat( &(*it) );
+ }
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if ((*it).m_mode == DNotifyMode) {
+ if ((*it).isDir && (ev == Deleted)) {
+ dList.append( &(*it) );
+
+ // must close the FD.
+ if ((*it).dn_fd) {
+ ::close((*it).dn_fd);
+ fd_Entry.remove((*it).dn_fd);
+ (*it).dn_fd = 0;
+ }
+ }
+
+ else if ((*it).isDir && (ev == Created)) {
+ // For created, but yet without DNOTIFYing ...
+ if ( (*it).dn_fd == 0) {
+ cList.append( &(*it) );
+ if (! useDNotify( &(*it) )) {
+ // if DNotify setup fails...
+ useStat( &(*it) );
+ }
+ }
+ }
+ }
+#endif
+
+ if ( ev != NoChange )
+ emitEvent( &(*it), ev);
+ }
+
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ // Scan parent of deleted directories for new creation
+ Entry* e;
+ for(e=dList.first();e;e=dList.next())
+ addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
+
+ // Remove watch of parent of new created directories
+ for(e=cList.first();e;e=cList.next())
+ removeEntry(0, TQDir::cleanDirPath( e->path+"/.."), e);
+#endif
+
+ if ( timerRunning )
+ timer->start(freq);
+
+ TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
+}
+
+bool KSimpleDirWatchPrivate::isNoisyFile( const char * filename )
+{
+ // $HOME/.X.err grows with debug output, so don't notify change
+ if ( *filename == '.') {
+ if (strncmp(filename, ".X.err", 6) == 0) return true;
+ if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
+ // fontconfig updates the cache on every KDE app start
+ // (inclusive kio_thumbnail slaves)
+ if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
+ }
+
+ return false;
+}
+
+#ifdef HAVE_FAM
+void KSimpleDirWatchPrivate::famEventReceived()
+{
+ static FAMEvent fe;
+
+ delayRemove = true;
+
+ while(use_fam && FAMPending(&fc)) {
+ if (FAMNextEvent(&fc, &fe) == -1) {
+ kdWarning(7001) << "FAM connection problem, switching to polling."
+ << endl;
+ use_fam = false;
+ delete sn; sn = 0;
+
+ // Replace all FAMMode entries with DNotify/Stat
+ EntryMap::Iterator it;
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
+#ifdef HAVE_INOTIFY
+ if (useINotify( &(*it) )) continue;
+#endif
+#ifdef HAVE_DNOTIFY
+ if (useDNotify( &(*it) )) continue;
+#endif
+ useStat( &(*it) );
+ }
+ }
+ else
+ checkFAMEvent(&fe);
+ }
+
+ TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
+}
+
+void KSimpleDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
+{
+ // Don't be too verbose ;-)
+ if ((fe->code == FAMExists) ||
+ (fe->code == FAMEndExist) ||
+ (fe->code == FAMAcknowledge)) return;
+
+ if ( isNoisyFile( fe->filename ) )
+ return;
+
+ Entry* e = 0;
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
+ FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
+ e = &(*it);
+ break;
+ }
+
+ // Entry* e = static_cast<Entry*>(fe->userdata);
+
+#if 0 // #88538
+ kdDebug(7001) << "Processing FAM event ("
+ << ((fe->code == FAMChanged) ? "FAMChanged" :
+ (fe->code == FAMDeleted) ? "FAMDeleted" :
+ (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
+ (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
+ (fe->code == FAMCreated) ? "FAMCreated" :
+ (fe->code == FAMMoved) ? "FAMMoved" :
+ (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
+ (fe->code == FAMExists) ? "FAMExists" :
+ (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
+ << ", " << fe->filename
+ << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
+ << ")" << endl;
+#endif
+
+ if (!e) {
+ // this happens e.g. for FAMAcknowledge after deleting a dir...
+ // kdDebug(7001) << "No entry for FAM event ?!" << endl;
+ return;
+ }
+
+ if (e->m_status == NonExistent) {
+ kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
+ return;
+ }
+
+ // Delayed handling. This rechecks changes with own stat calls.
+ e->dirty = true;
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true);
+
+ // needed FAM control actions on FAM events
+ if (e->isDir)
+ switch (fe->code)
+ {
+ case FAMDeleted:
+ // file absolute: watched dir
+ if (!TQDir::isRelativePath(fe->filename))
+ {
+ // a watched directory was deleted
+
+ e->m_status = NonExistent;
+ FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
+ kdDebug(7001) << "Cancelled FAMReq "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << " for " << e->path << endl;
+ // Scan parent for a new creation
+ addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
+ }
+ break;
+
+ case FAMCreated: {
+ // check for creation of a directory we have to watch
+ Entry *sub_entry = e->m_entries.first();
+ for(;sub_entry; sub_entry = e->m_entries.next())
+ if (sub_entry->path == e->path + "/" + fe->filename) break;
+ if (sub_entry && sub_entry->isDir) {
+ TQString path = e->path;
+ removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
+ sub_entry->m_status = Normal;
+ if (!useFAM(sub_entry))
+#ifdef HAVE_INOTIFY
+ if (!useINotify(sub_entry ))
+#endif
+ useStat(sub_entry);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+#else
+void KSimpleDirWatchPrivate::famEventReceived() {}
+#endif
+
+
+void KSimpleDirWatchPrivate::statistics()
+{
+ EntryMap::Iterator it;
+
+ kdDebug(7001) << "Entries watched:" << endl;
+ if (m_mapEntries.count()==0) {
+ kdDebug(7001) << " None." << endl;
+ }
+ else {
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Entry* e = &(*it);
+ kdDebug(7001) << " " << e->path << " ("
+ << ((e->m_status==Normal)?"":"Nonexistent ")
+ << (e->isDir ? "Dir":"File") << ", using "
+ << ((e->m_mode == FAMMode) ? "FAM" :
+ (e->m_mode == INotifyMode) ? "INotify" :
+ (e->m_mode == DNotifyMode) ? "DNotify" :
+ (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
+ << ")" << endl;
+
+ Client* c = e->m_clients.first();
+ for(;c; c = e->m_clients.next()) {
+ TQString pending;
+ if (c->watchingStopped) {
+ if (c->pending & Deleted) pending += "deleted ";
+ if (c->pending & Created) pending += "created ";
+ if (c->pending & Changed) pending += "changed ";
+ if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
+ pending = ", stopped" + pending;
+ }
+ kdDebug(7001) << " by " << c->instance->name()
+ << " (" << c->count << " times)"
+ << pending << endl;
+ }
+ if (e->m_entries.count()>0) {
+ kdDebug(7001) << " dependent entries:" << endl;
+ Entry* d = e->m_entries.first();
+ for(;d; d = e->m_entries.next()) {
+ kdDebug(7001) << " " << d << endl;
+ kdDebug(7001) << " " << d->path << " (" << d << ") " << endl;
+ }
+ }
+ }
+ }
+}
+
+
+//
+// Class KSimpleDirWatch
+//
+
+static KStaticDeleter<KSimpleDirWatch> sd_dw;
+KSimpleDirWatch* KSimpleDirWatch::s_pSelf = 0L;
+
+KSimpleDirWatch* KSimpleDirWatch::self()
+{
+ if ( !s_pSelf ) {
+ sd_dw.setObject( s_pSelf, new KSimpleDirWatch );
+ }
+
+ return s_pSelf;
+}
+
+bool KSimpleDirWatch::exists()
+{
+ return s_pSelf != 0;
+}
+
+KSimpleDirWatch::KSimpleDirWatch (TQObject* parent, const char* name)
+ : TQObject(parent,name)
+{
+ if (!name) {
+ static int nameCounter = 0;
+
+ nameCounter++;
+ setName(TQString(TQString("KSimpleDirWatch-%1").arg(nameCounter)).ascii());
+ }
+
+ if (!dwp_self)
+ dwp_self = new KSimpleDirWatchPrivate;
+ d = dwp_self;
+ d->ref();
+
+ _isStopped = false;
+}
+
+KSimpleDirWatch::~KSimpleDirWatch()
+{
+ d->removeEntries(this);
+ if ( d->deref() )
+ {
+ // delete it if it's the last one
+ delete d;
+ dwp_self = 0L;
+ }
+}
+
+
+// TODO: add watchFiles/recursive support
+void KSimpleDirWatch::addDir( const TQString& _path,
+ bool watchFiles, bool recursive)
+{
+ if (watchFiles || recursive) {
+ kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
+ }
+ if (d) d->addEntry(this, _path, 0, true);
+}
+
+void KSimpleDirWatch::addFile( const TQString& _path )
+{
+ if (d) d->addEntry(this, _path, 0, false);
+}
+
+TQDateTime KSimpleDirWatch::ctime( const TQString &_path )
+{
+ KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
+
+ if (!e)
+ return TQDateTime();
+
+ TQDateTime result;
+ result.setTime_t(e->m_ctime);
+ return result;
+}
+
+void KSimpleDirWatch::removeDir( const TQString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+void KSimpleDirWatch::removeFile( const TQString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+bool KSimpleDirWatch::stopDirScan( const TQString& _path )
+{
+ if (d) {
+ KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir) return d->stopEntryScan(this, e);
+ }
+ return false;
+}
+
+bool KSimpleDirWatch::restartDirScan( const TQString& _path )
+{
+ if (d) {
+ KSimpleDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir)
+ // restart without notifying pending events
+ return d->restartEntryScan(this, e, false);
+ }
+ return false;
+}
+
+void KSimpleDirWatch::stopScan()
+{
+ if (d) d->stopScan(this);
+ _isStopped = true;
+}
+
+void KSimpleDirWatch::startScan( bool notify, bool skippedToo )
+{
+ _isStopped = false;
+ if (d) d->startScan(this, notify, skippedToo);
+}
+
+
+bool KSimpleDirWatch::contains( const TQString& _path ) const
+{
+ KSimpleDirWatchPrivate::Entry* e = d->entry(_path);
+ if (!e)
+ return false;
+
+ KSimpleDirWatchPrivate::Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next())
+ if (c->instance == this) return true;
+
+ return false;
+}
+
+void KSimpleDirWatch::statistics()
+{
+ if (!dwp_self) {
+ kdDebug(7001) << "KSimpleDirWatch not used" << endl;
+ return;
+ }
+ dwp_self->statistics();
+}
+
+
+void KSimpleDirWatch::setCreated( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting created " << _file << endl;
+ emit created( _file );
+}
+
+void KSimpleDirWatch::setDirty( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting dirty " << _file << endl;
+ emit dirty( _file );
+}
+
+void KSimpleDirWatch::setDeleted( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting deleted " << _file << endl;
+ emit deleted( _file );
+}
+
+KSimpleDirWatch::Method KSimpleDirWatch::internalMethod()
+{
+#ifdef HAVE_FAM
+ if (d->use_fam)
+ return KSimpleDirWatch::FAM;
+#endif
+#ifdef HAVE_INOTIFY
+ if (d->supports_inotify)
+ return KSimpleDirWatch::INotify;
+#endif
+#ifdef HAVE_DNOTIFY
+ if (d->supports_dnotify)
+ return KSimpleDirWatch::DNotify;
+#endif
+ return KSimpleDirWatch::Stat;
+}
+
+
+#include "ksimpledirwatch.moc"
+#include "ksimpledirwatch_p.moc"
+
+//sven
+
+// vim: sw=2 ts=8 et
diff --git a/tdecore/ksimpledirwatch.h b/tdecore/ksimpledirwatch.h
new file mode 100644
index 000000000..36ceafba6
--- /dev/null
+++ b/tdecore/ksimpledirwatch.h
@@ -0,0 +1,293 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998 Sven Radej <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef _KSIMPLEDIRWATCH_H
+#define _KSIMPLEDIRWATCH_H
+
+#include <tqtimer.h>
+#include <tqdatetime.h>
+#include <tqmap.h>
+
+#include <tdelibs_export.h>
+
+#define kdirwatch KSimpleDirWatch::self()
+
+class KSimpleDirWatchPrivate;
+
+ /**
+ * KSimpleDirWatch is a basic copy of KDirWatch
+ * but with the KIO linking requirement removed
+ *
+ * Watch directories and files for changes.
+ * The watched directories or files don't have to exist yet.
+ *
+ * When a watched directory is changed, i.e. when files therein are
+ * created or deleted, KSimpleDirWatch will emit the signal dirty().
+ *
+ * When a watched, but previously not existing directory gets created,
+ * KSimpleDirWatch will emit the signal created().
+ *
+ * When a watched directory gets deleted, KSimpleDirWatch will emit the
+ * signal deleted(). The directory is still watched for new
+ * creation.
+ *
+ * When a watched file is changed, i.e. attributes changed or written
+ * to, KSimpleDirWatch will emit the signal dirty().
+ *
+ * Scanning of particular directories or files can be stopped temporarily
+ * and restarted. The whole class can be stopped and restarted.
+ * Directories and files can be added/removed from the list in any state.
+ *
+ * The implementation uses the FAM service when available;
+ * if FAM is not available, the DNOTIFY functionality is used on LINUX.
+ * As a last resort, a regular polling for change of modification times
+ * is done; the polling interval is a global config option:
+ * DirWatch/PollInterval and DirWatch/NFSPollInterval for NFS mounted
+ * directories.
+ *
+ * @see self()
+ * @short Class for watching directory and file changes.
+ * @author Sven Radej <[email protected]>
+ */
+class KIO_EXPORT KSimpleDirWatch : public TQObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ *
+ * Scanning begins immediately when a dir/file watch
+ * is added.
+ * @param parent the parent of the TQObject (or 0 for parent-less KDataTools)
+ * @param name the name of the TQObject, can be 0
+ */
+ KSimpleDirWatch (TQObject* parent = 0, const char* name = 0);
+
+ /**
+ * Destructor.
+ *
+ * Stops scanning and cleans up.
+ */
+ ~KSimpleDirWatch();
+
+ /**
+ * Adds a directory to be watched.
+ *
+ * The directory does not have to exist. When @p watchFiles is
+ * false (the default), the signals dirty(), created(), deleted()
+ * can be emitted, all for the watched directory.
+ * When @p watchFiles is true, all files in the watched directory
+ * are watched for changes, too. Thus, the signals dirty(),
+ * created(), deleted() can be emitted.
+ *
+ * @param path the path to watch
+ * @param watchFiles if true, the KSimpleDirWatch will also watch files - NOT IMPLEMENTED YET
+ * @param recursive if true, all sub directories are also watched - NOT IMPLEMENTED YET
+ */
+ void addDir(const TQString& path,
+ bool watchFiles = false, bool recursive = false);
+
+ /**
+ * Adds a file to be watched.
+ * @param file the file to watch
+ */
+ void addFile(const TQString& file);
+
+ /**
+ * Returns the time the directory/file was last changed.
+ * @param path the file to check
+ * @return the date of the last modification
+ */
+ TQDateTime ctime(const TQString& path);
+
+ /**
+ * Removes a directory from the list of scanned directories.
+ *
+ * If specified path is not in the list this does nothing.
+ * @param path the path of the dir to be removed from the list
+ */
+ void removeDir(const TQString& path);
+
+ /**
+ * Removes a file from the list of watched files.
+ *
+ * If specified path is not in the list this does nothing.
+ * @param file the file to be removed from the list
+ */
+ void removeFile(const TQString& file);
+
+ /**
+ * Stops scanning the specified path.
+ *
+ * The @p path is not deleted from the interal just, it is just skipped.
+ * Call this function when you perform an huge operation
+ * on this directory (copy/move big files or many files). When finished,
+ * call restartDirScan(path).
+ *
+ * @param path the path to skip
+ * @return true if the @p path is being watched, otherwise false
+ * @see restartDirScanning()
+ */
+ bool stopDirScan(const TQString& path);
+
+ /**
+ * Restarts scanning for specified path.
+ *
+ * Resets ctime. It doesn't notify
+ * the change (by emitted a signal), since the ctime value is reset.
+ *
+ * Call it when you are finished with big operations on that path,
+ * @em and when @em you have refreshed that path.
+ *
+ * @param path the path to restart scanning
+ * @return true if the @p path is being watched, otherwise false
+ * @see stopDirScanning()
+ */
+ bool restartDirScan(const TQString& path);
+
+ /**
+ * Starts scanning of all dirs in list.
+ *
+ * @param notify If true, all changed directories (since
+ * stopScan() call) will be notified for refresh. If notify is
+ * false, all ctimes will be reset (except those who are stopped,
+ * but only if @p skippedToo is false) and changed dirs won't be
+ * notified. You can start scanning even if the list is
+ * empty. First call should be called with @p false or else all
+ * directories
+ * in list will be notified.
+ * @param skippedToo if true, the skipped directoris (scanning of which was
+ * stopped with stopDirScan() ) will be reset and notified
+ * for change. Otherwise, stopped directories will continue to be
+ * unnotified.
+ */
+ void startScan( bool notify=false, bool skippedToo=false );
+
+ /**
+ * Stops scanning of all directories in internal list.
+ *
+ * The timer is stopped, but the list is not cleared.
+ */
+ void stopScan();
+
+ /**
+ * Is scanning stopped?
+ * After creation of a KSimpleDirWatch instance, this is false.
+ * @return true when scanning stopped
+ */
+ bool isStopped() { return _isStopped; }
+
+ /**
+ * Check if a directory is being watched by this KSimpleDirWatch instance
+ * @param path the directory to check
+ * @return true if the directory is being watched
+ */
+ bool contains( const TQString& path ) const;
+
+ /**
+ * Dump statistic information about all KSimpleDirWatch instances.
+ * This checks for consistency, too.
+ */
+ static void statistics();
+
+ /**
+ * Emits created().
+ * @param path the path of the file or directory
+ */
+ void setCreated( const TQString &path );
+ /**
+ * Emits dirty().
+ * @param path the path of the file or directory
+ */
+ void setDirty( const TQString &path );
+ /**
+ * Emits deleted().
+ * @param path the path of the file or directory
+ */
+ void setDeleted( const TQString &path );
+
+ enum Method { FAM, DNotify, Stat, INotify };
+ /**
+ * Returns the preferred internal method to
+ * watch for changes.
+ * @since 3.2
+ */
+ Method internalMethod();
+
+ /**
+ * The KSimpleDirWatch instance usually globally used in an application.
+ * It is automatically deleted when the application exits.
+ *
+ * However, you can create an arbitrary number of KSimpleDirWatch instances
+ * aside from this one - for those you have to take care of memory management.
+ *
+ * This function returns an instance of KSimpleDirWatch. If there is none, it
+ * will be created.
+ *
+ * @return a KSimpleDirWatch instance
+ */
+ static KSimpleDirWatch* self();
+ /**
+ * Returns true if there is an instance of KSimpleDirWatch.
+ * @return true if there is an instance of KSimpleDirWatch.
+ * @see KSimpleDirWatch::self()
+ * @since 3.1
+ */
+ static bool exists();
+
+ signals:
+
+ /**
+ * Emitted when a watched object is changed.
+ * For a directory this signal is emitted when files
+ * therein are created or deleted.
+ * For a file this signal is emitted when its size or attributes change.
+ *
+ * When you watch a directory, changes in the size or attributes of
+ * contained files may or may not trigger this signal to be emitted
+ * depending on which backend is used by KSimpleDirWatch.
+ *
+ * The new ctime is set before the signal is emitted.
+ * @param path the path of the file or directory
+ */
+ void dirty (const TQString &path);
+
+ /**
+ * Emitted when a file or directory is created.
+ * @param path the path of the file or directory
+ */
+ void created (const TQString &path );
+
+ /**
+ * Emitted when a file or directory is deleted.
+ *
+ * The object is still watched for new creation.
+ * @param path the path of the file or directory
+ */
+ void deleted (const TQString &path );
+
+ private:
+ bool _isStopped;
+
+ KSimpleDirWatchPrivate *d;
+ static KSimpleDirWatch* s_pSelf;
+};
+
+#endif
+
+// vim: sw=3 et
diff --git a/tdecore/ksimpledirwatch_p.h b/tdecore/ksimpledirwatch_p.h
new file mode 100644
index 000000000..a0022f3ae
--- /dev/null
+++ b/tdecore/ksimpledirwatch_p.h
@@ -0,0 +1,161 @@
+/* Private Header for class of KSimpleDirWatchPrivate
+ *
+ * this separate header file is needed for TQMOC processing
+ * because KSimpleDirWatchPrivate has signals and slots
+ *
+ * KSimpleDirWatch is a basic copy of KDirWatch
+ * but with the KIO linking requirement removed
+ */
+
+#ifndef _KSIMPLEDIRWATCH_P_H
+#define _KSIMPLEDIRWATCH_P_H
+
+#ifdef HAVE_FAM
+#include <fam.h>
+#endif
+
+#include <ctime>
+
+#define invalid_ctime ((time_t)-1)
+
+/* KSimpleDirWatchPrivate is a singleton and does the watching
+ * for every KSimpleDirWatch instance in the application.
+ */
+class KSimpleDirWatchPrivate : public TQObject
+{
+ Q_OBJECT
+public:
+
+ enum entryStatus { Normal = 0, NonExistent };
+ enum entryMode { UnknownMode = 0, StatMode, DNotifyMode, INotifyMode, FAMMode };
+ enum { NoChange=0, Changed=1, Created=2, Deleted=4 };
+
+ struct Client {
+ KSimpleDirWatch* instance;
+ int count;
+ // did the instance stop watching
+ bool watchingStopped;
+ // events blocked when stopped
+ int pending;
+ };
+
+ class Entry
+ {
+ public:
+ // the last observed modification time
+ time_t m_ctime;
+ // the last observed link count
+ int m_nlink;
+ entryStatus m_status;
+ entryMode m_mode;
+ bool isDir;
+ // instances interested in events
+ TQPtrList<Client> m_clients;
+ // nonexistent entries of this directory
+ TQPtrList<Entry> m_entries;
+ TQString path;
+
+ int msecLeft, freq;
+
+ void addClient(KSimpleDirWatch*);
+ void removeClient(KSimpleDirWatch*);
+ int clients();
+ bool isValid() { return m_clients.count() || m_entries.count(); }
+
+ bool dirty;
+ void propagate_dirty();
+
+#ifdef HAVE_FAM
+ FAMRequest fr;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ int dn_fd;
+#endif
+#ifdef HAVE_INOTIFY
+ int wd;
+#endif
+ };
+
+ typedef TQMap<TQString,Entry> EntryMap;
+
+ KSimpleDirWatchPrivate();
+ ~KSimpleDirWatchPrivate();
+
+ void resetList (KSimpleDirWatch*,bool);
+ void useFreq(Entry* e, int newFreq);
+ void addEntry(KSimpleDirWatch*,const TQString&, Entry*, bool);
+ void removeEntry(KSimpleDirWatch*,const TQString&, Entry*);
+ bool stopEntryScan(KSimpleDirWatch*, Entry*);
+ bool restartEntryScan(KSimpleDirWatch*, Entry*, bool );
+ void stopScan(KSimpleDirWatch*);
+ void startScan(KSimpleDirWatch*, bool, bool);
+
+ void removeEntries(KSimpleDirWatch*);
+ void statistics();
+
+ Entry* entry(const TQString&);
+ int scanEntry(Entry* e);
+ void emitEvent(Entry* e, int event, const TQString &fileName = TQString::null);
+
+ // Memory management - delete when last KSimpleDirWatch gets deleted
+ void ref() { m_ref++; }
+ bool deref() { return ( --m_ref == 0 ); }
+
+ static bool isNoisyFile( const char *filename );
+
+public slots:
+ void slotRescan();
+ void famEventReceived(); // for FAM
+ void slotActivated(); // for DNOTIFY
+ void slotRemoveDelayed();
+
+public:
+ TQTimer *timer;
+ EntryMap m_mapEntries;
+
+ int freq;
+ int statEntries;
+ int m_nfsPollInterval, m_PollInterval;
+ int m_ref;
+ bool useStat(Entry*);
+
+ bool delayRemove;
+ TQPtrList<Entry> removeList;
+
+ bool rescan_all;
+ TQTimer rescan_timer;
+
+#ifdef HAVE_FAM
+ TQSocketNotifier *sn;
+ FAMConnection fc;
+ bool use_fam;
+
+ void checkFAMEvent(FAMEvent*);
+ bool useFAM(Entry*);
+#endif
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ TQSocketNotifier *mSn;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ bool supports_dnotify;
+ int mPipe[2];
+ TQIntDict<Entry> fd_Entry;
+
+ static void dnotify_handler(int, siginfo_t *si, void *);
+ static void dnotify_sigio_handler(int, siginfo_t *si, void *);
+ bool useDNotify(Entry*);
+#endif
+
+#ifdef HAVE_INOTIFY
+ bool supports_inotify;
+ int m_inotify_fd;
+
+ bool useINotify(Entry*);
+#endif
+};
+
+#endif // KSIMPLEDIRWATCH_P_H
+
diff --git a/tdecore/tdehardwaredevices.cpp b/tdecore/tdehardwaredevices.cpp
index ba2fc5bfb..e62abeb37 100644
--- a/tdecore/tdehardwaredevices.cpp
+++ b/tdecore/tdehardwaredevices.cpp
@@ -27,6 +27,7 @@
#include <klocale.h>
#include <kconfig.h>
#include <ktempfile.h>
+#include <ksimpledirwatch.h>
#include <kstandarddirs.h>
#include <libudev.h>
@@ -37,10 +38,31 @@
#include <sys/ioctl.h>
#include <linux/fs.h>
+// BEGIN BLOCK
+// Copied from include/linux/genhd.h
+#define GENHD_FL_REMOVABLE 1
+#define GENHD_FL_MEDIA_CHANGE_NOTIFY 4
+#define GENHD_FL_CD 8
+#define GENHD_FL_UP 16
+#define GENHD_FL_SUPPRESS_PARTITION_INFO 32
+#define GENHD_FL_EXT_DEVT 64
+#define GENHD_FL_NATIVE_CAPACITY 128
+#define GENHD_FL_BLOCK_EVENTS_ON_EXCL_WRITE 256
+// END BLOCK
+
// NOTE TO DEVELOPERS
// This command will greatly help when attempting to find properties to distinguish one device from another
// udevadm info --query=all --path=/sys/....
+TDESensorCluster::TDESensorCluster() {
+ label = TQString::null;
+ current = -1;
+ minimum = -1;
+ maximum = -1;
+ warning = -1;
+ critical = -1;
+}
+
TDEGenericDevice::TDEGenericDevice(TDEGenericDeviceType::TDEGenericDeviceType dt, TQString dn) {
m_deviceType = dt;
m_deviceName = dn;
@@ -128,6 +150,22 @@ void TDEGenericDevice::setModelID(TQString id) {
m_modelID.replace("0x", "");
}
+TQString &TDEGenericDevice::vendorEncoded() {
+ return m_vendorenc;
+}
+
+void TDEGenericDevice::setVendorEncoded(TQString id) {
+ m_vendorenc = id;
+}
+
+TQString &TDEGenericDevice::modelEncoded() {
+ return m_modelenc;
+}
+
+void TDEGenericDevice::setModelEncoded(TQString id) {
+ m_modelenc = id;
+}
+
TQString &TDEGenericDevice::subVendorID() {
return m_subvendorID;
}
@@ -223,6 +261,20 @@ TQString TDEGenericDevice::friendlyName() {
else if (m_modAlias.lower().startsWith("usb")) {
m_friendlyName = KGlobal::hardwareDevices()->findUSBDeviceName(m_vendorID, m_modelID, m_subvendorID, m_submodelID);
}
+ else {
+ TQString acpigentype = systemPath();
+ acpigentype.remove(0, acpigentype.findRev("/")+1);
+ TQString pnpgentype = acpigentype;
+ pnpgentype.truncate(pnpgentype.find(":"));
+ if (pnpgentype.startsWith("PNP")) {
+ m_friendlyName = KGlobal::hardwareDevices()->findPNPDeviceName(pnpgentype);
+ }
+ else if (acpigentype.startsWith("device:")) {
+ acpigentype.remove(0, acpigentype.findRev(":")+1);
+ acpigentype.prepend("0x");
+ m_friendlyName = i18n("ACPI Node %1").arg(acpigentype.toUInt(0,0));
+ }
+ }
}
if (m_friendlyName.isNull()) {
@@ -231,6 +283,24 @@ TQString TDEGenericDevice::friendlyName() {
if (type() == TDEGenericDeviceType::CPU) {
m_friendlyName = name();
}
+ else if (type() == TDEGenericDeviceType::Event) {
+ // Use parent node name
+ if (m_parentDevice) {
+ return m_parentDevice->friendlyName();
+ }
+ else {
+ m_friendlyName = i18n("Generic Event Device");
+ }
+ }
+ else if (type() == TDEGenericDeviceType::Input) {
+ // Use parent node name
+ if (m_parentDevice) {
+ return m_parentDevice->friendlyName();
+ }
+ else {
+ m_friendlyName = i18n("Generic Input Device");
+ }
+ }
// Guess by driver
else if (!m_deviceDriver.isNull()) {
TQString friendlyDriverName = m_deviceDriver.lower();
@@ -345,6 +415,27 @@ void TDEStorageDevice::setSlaveDevices(TQStringList sd) {
}
TQString TDEStorageDevice::friendlyName() {
+ // Return the actual storage device name
+ TQString devicevendorid = vendorEncoded();
+ TQString devicemodelid = modelEncoded();
+
+ devicevendorid.replace("\\x20", " ");
+ devicemodelid.replace("\\x20", " ");
+
+ devicevendorid = devicevendorid.stripWhiteSpace();
+ devicemodelid = devicemodelid.stripWhiteSpace();
+ devicevendorid = devicevendorid.simplifyWhiteSpace();
+ devicemodelid = devicemodelid.simplifyWhiteSpace();
+
+ TQString devicename = devicevendorid + " " + devicemodelid;
+
+ devicename = devicename.stripWhiteSpace();
+ devicename = devicename.simplifyWhiteSpace();
+
+ if (devicename != "") {
+ return devicename;
+ }
+
if (isDiskOfType(TDEDiskDeviceType::Floppy)) {
return friendlyDeviceType();
}
@@ -352,7 +443,7 @@ TQString TDEStorageDevice::friendlyName() {
TQString label = diskLabel();
if (label.isNull()) {
if (deviceSize() > 0) {
- if (checkDiskStatus(TDEDiskDeviceStatus::Removable)) {
+ if (checkDiskStatus(TDEDiskDeviceStatus::Hotpluggable)) {
label = i18n("%1 Removable Device").arg(deviceFriendlySize());
}
else {
@@ -399,7 +490,7 @@ TQString TDEStorageDevice::friendlyDeviceType() {
if (isDiskOfType(TDEDiskDeviceType::HDD)) {
ret = i18n("Hard Disk Drive");
- if (checkDiskStatus(TDEDiskDeviceStatus::Removable)) {
+ if (checkDiskStatus(TDEDiskDeviceStatus::Hotpluggable)) {
ret = i18n("Removable Storage");
}
if (isDiskOfType(TDEDiskDeviceType::CompactFlash)) {
@@ -447,7 +538,7 @@ TQPixmap TDEStorageDevice::icon(KIcon::StdSizes size) {
if (isDiskOfType(TDEDiskDeviceType::HDD)) {
ret = DesktopIcon("hdd_unmount", size);
- if (checkDiskStatus(TDEDiskDeviceStatus::Removable)) {
+ if (checkDiskStatus(TDEDiskDeviceStatus::Hotpluggable)) {
ret = DesktopIcon("usbpendrive_unmount", size);
}
if (isDiskOfType(TDEDiskDeviceType::CompactFlash)) {
@@ -710,10 +801,95 @@ bool TDEStorageDevice::unmountDevice(TQString* errRet, int* retcode) {
return false;
}
+TDECPUDevice::TDECPUDevice(TDEGenericDeviceType::TDEGenericDeviceType dt, TQString dn) : TDEGenericDevice(dt, dn) {
+}
+
+TDECPUDevice::~TDECPUDevice() {
+}
+
+double &TDECPUDevice::frequency() {
+ return m_frequency;
+}
+
+void TDECPUDevice::setFrequency(double fr) {
+ m_frequency = fr;
+}
+
+double &TDECPUDevice::minFrequency() {
+ return m_minfrequency;
+}
+
+void TDECPUDevice::setMinFrequency(double fr) {
+ m_minfrequency = fr;
+}
+
+double &TDECPUDevice::maxFrequency() {
+ return m_maxfrequency;
+}
+
+void TDECPUDevice::setMaxFrequency(double fr) {
+ m_maxfrequency = fr;
+}
+
+double &TDECPUDevice::transitionLatency() {
+ return m_transitionlatency;
+}
+
+void TDECPUDevice::setTransitionLatency(double tl) {
+ m_transitionlatency = tl;
+}
+
+TQString &TDECPUDevice::governor() {
+ return m_governor;
+}
+
+void TDECPUDevice::setGovernor(TQString gr) {
+ m_governor = gr;
+}
+
+TQString &TDECPUDevice::scalingDriver() {
+ return m_scalingdriver;
+}
+
+void TDECPUDevice::setScalingDriver(TQString dr) {
+ m_scalingdriver = dr;
+}
+
+TQStringList &TDECPUDevice::dependentProcessors() {
+ return m_tiedprocs;
+}
+
+void TDECPUDevice::setDependentProcessors(TQStringList dp) {
+ m_tiedprocs = dp;
+}
+
+TQStringList &TDECPUDevice::availableFrequencies() {
+ return m_frequencies;
+}
+
+void TDECPUDevice::setAvailableFrequencies(TQStringList af) {
+ m_frequencies = af;
+}
+
+TDESensorDevice::TDESensorDevice(TDEGenericDeviceType::TDEGenericDeviceType dt, TQString dn) : TDEGenericDevice(dt, dn) {
+}
+
+TDESensorDevice::~TDESensorDevice() {
+}
+
+TDESensorClusterMap TDESensorDevice::values() {
+ return m_sensorValues;
+}
+
+void TDESensorDevice::setValues(TDESensorClusterMap cl) {
+ m_sensorValues = cl;
+}
+
TDEHardwareDevices::TDEHardwareDevices() {
// Initialize members
pci_id_map = 0;
usb_id_map = 0;
+ pnp_id_map = 0;
// Set up device list
m_deviceList.setAutoDelete( TRUE ); // the list owns the objects
@@ -750,12 +926,49 @@ TDEHardwareDevices::TDEHardwareDevices() {
m_mountScanNotifier = new TQSocketNotifier(m_procMountsFd, TQSocketNotifier::Exception, this);
connect( m_mountScanNotifier, TQT_SIGNAL(activated(int)), this, TQT_SLOT(processModifiedMounts()) );
+ // Read in the current cpu information
+ // Yes, a race condition exists between this and the cpu monitor start below, but it shouldn't be a problem 99.99% of the time
+ m_cpuInfo.clear();
+ TQFile cpufile( "/proc/cpuinfo" );
+ if ( cpufile.open( IO_ReadOnly ) ) {
+ TQTextStream stream( &cpufile );
+ while ( !stream.atEnd() ) {
+ m_cpuInfo.append(stream.readLine());
+ }
+ cpufile.close();
+ }
+
+// [FIXME 0.01]
+// Apparently the Linux kernel just does not notify userspace applications of CPU frequency changes
+// This is STUPID, as it means I have to poll the CPU information structures with a 0.5 second or so timer just to keep the information up to date
+#if 0
+ // Monitor for changed cpu information
+ // Watched directories are set up during the initial CPU scan
+ m_cpuWatch = new KSimpleDirWatch(this);
+ connect( m_cpuWatch, TQT_SIGNAL(dirty(const TQString &)), this, TQT_SLOT(processModifiedCPUs()) );
+#else
+ m_cpuWatchTimer = new TQTimer(this);
+ connect( m_cpuWatchTimer, SIGNAL(timeout()), this, SLOT(processModifiedCPUs()) );
+ TQDir nodezerocpufreq("/sys/devices/system/cpu/cpu0/cpufreq");
+ if (nodezerocpufreq.exists()) {
+ m_cpuWatchTimer->start( 500, FALSE ); // 0.5 second repeating timer
+ }
+#endif
+
// Update internal device information
queryHardwareInformation();
}
}
TDEHardwareDevices::~TDEHardwareDevices() {
+// [FIXME 0.01]
+#if 0
+ // Stop CPU scanning
+ m_cpuWatch->stopScan();
+#else
+ m_cpuWatchTimer->stop();
+#endif
+
// Stop mount scanning
close(m_procMountsFd);
@@ -769,6 +982,9 @@ TDEHardwareDevices::~TDEHardwareDevices() {
if (usb_id_map) {
delete usb_id_map;
}
+ if (pnp_id_map) {
+ delete pnp_id_map;
+ }
}
void TDEHardwareDevices::rescanDeviceInformation(TDEGenericDevice* hwdevice) {
@@ -885,6 +1101,158 @@ void TDEHardwareDevices::processHotPluggedHardware() {
}
}
+void TDEHardwareDevices::processModifiedCPUs() {
+ // Detect what changed between the old cpu information and the new information,
+ // and emit appropriate events
+
+ // Read new CPU information table
+ m_cpuInfo.clear();
+ TQFile cpufile( "/proc/cpuinfo" );
+ if ( cpufile.open( IO_ReadOnly ) ) {
+ TQTextStream stream( &cpufile );
+ while ( !stream.atEnd() ) {
+ m_cpuInfo.append(stream.readLine());
+ }
+ cpufile.close();
+ }
+
+ // Parse CPU information table
+ TDECPUDevice *cdevice;
+ cdevice = 0;
+ bool modified = false;
+
+ TQString curline;
+ int processorNumber = 0;
+ int processorCount = 0;
+ for (TQStringList::Iterator cpuit = m_cpuInfo.begin(); cpuit != m_cpuInfo.end(); ++cpuit) {
+ // WARNING This routine assumes that "processor" is always the first entry in /proc/cpuinfo!
+ curline = *cpuit;
+ if (curline.startsWith("processor")) {
+ curline.remove(0, curline.find(":")+1);
+ curline = curline.stripWhiteSpace();
+ processorNumber = curline.toInt();
+ cdevice = dynamic_cast<TDECPUDevice*>(findBySystemPath(TQString("/sys/devices/system/cpu/cpu%1").arg(processorNumber)));
+ }
+ if (curline.startsWith("model name")) {
+ curline.remove(0, curline.find(":")+1);
+ curline = curline.stripWhiteSpace();
+ if (cdevice->name() != curline) modified = true;
+ cdevice->setName(curline);
+ }
+ if (curline.startsWith("cpu MHz")) {
+ curline.remove(0, curline.find(":")+1);
+ curline = curline.stripWhiteSpace();
+ if (cdevice->frequency() != curline.toDouble()) modified = true;
+ cdevice->setFrequency(curline.toDouble());
+ }
+ if (curline.startsWith("vendor_id")) {
+ curline.remove(0, curline.find(":")+1);
+ curline = curline.stripWhiteSpace();
+ if (cdevice->vendorName() != curline) modified = true;
+ cdevice->setVendorName(curline);
+ if (cdevice->vendorEncoded() != curline) modified = true;
+ cdevice->setVendorEncoded(curline);
+ }
+ }
+
+ processorCount = processorNumber+1;
+
+ // Read in other information from cpufreq, if available
+ for (processorNumber=0; processorNumber<processorCount; processorNumber++) {
+ cdevice = dynamic_cast<TDECPUDevice*>(findBySystemPath(TQString("/sys/devices/system/cpu/cpu%1").arg(processorNumber)));
+ TQDir cpufreq_dir(TQString("/sys/devices/system/cpu/cpu%1/cpufreq").arg(processorNumber));
+ TQString scalinggovernor;
+ TQString scalingdriver;
+ double minfrequency = -1;
+ double maxfrequency = -1;
+ double trlatency = -1;
+ TQStringList affectedcpulist;
+ TQStringList frequencylist;
+ if (cpufreq_dir.exists()) {
+ TQString nodename = cpufreq_dir.path();
+ nodename.append("/scaling_governor");
+ TQFile scalinggovernorfile(nodename);
+ if (scalinggovernorfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &scalinggovernorfile );
+ scalinggovernor = stream.readLine();
+ scalinggovernorfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/scaling_driver");
+ TQFile scalingdriverfile(nodename);
+ if (scalingdriverfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &scalingdriverfile );
+ scalingdriver = stream.readLine();
+ scalingdriverfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/scaling_min_freq");
+ TQFile minfrequencyfile(nodename);
+ if (minfrequencyfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &minfrequencyfile );
+ minfrequency = stream.readLine().toDouble()/1000.0;
+ minfrequencyfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/scaling_max_freq");
+ TQFile maxfrequencyfile(nodename);
+ if (maxfrequencyfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &maxfrequencyfile );
+ maxfrequency = stream.readLine().toDouble()/1000.0;
+ maxfrequencyfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/cpuinfo_transition_latency");
+ TQFile trlatencyfile(nodename);
+ if (trlatencyfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &trlatencyfile );
+ trlatency = stream.readLine().toDouble()/1000.0;
+ trlatencyfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/affected_cpus");
+ TQFile tiedcpusfile(nodename);
+ if (tiedcpusfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &tiedcpusfile );
+ affectedcpulist = TQStringList::split(" ", stream.readLine());
+ tiedcpusfile.close();
+ }
+ nodename = cpufreq_dir.path();
+ nodename.append("/scaling_available_frequencies");
+ TQFile availfreqsfile(nodename);
+ if (availfreqsfile.open(IO_ReadOnly)) {
+ TQTextStream stream( &availfreqsfile );
+ frequencylist = TQStringList::split(" ", stream.readLine());
+ availfreqsfile.close();
+ }
+ }
+
+ // Update CPU information structure
+ if (cdevice->governor() != scalinggovernor) modified = true;
+ cdevice->setGovernor(scalinggovernor);
+ if (cdevice->scalingDriver() != scalingdriver) modified = true;
+ cdevice->setScalingDriver(scalingdriver);
+ if (cdevice->minFrequency() != minfrequency) modified = true;
+ cdevice->setMinFrequency(minfrequency);
+ if (cdevice->maxFrequency() != maxfrequency) modified = true;
+ cdevice->setMaxFrequency(maxfrequency);
+ if (cdevice->transitionLatency() != trlatency) modified = true;
+ cdevice->setTransitionLatency(trlatency);
+ if (cdevice->dependentProcessors().join(" ") != affectedcpulist.join(" ")) modified = true;
+ cdevice->setDependentProcessors(affectedcpulist);
+ if (cdevice->availableFrequencies().join(" ") != frequencylist.join(" ")) modified = true;
+ cdevice->setAvailableFrequencies(frequencylist);
+ }
+
+ if (modified) {
+ for (processorNumber=0; processorNumber<processorCount; processorNumber++) {
+ TDEGenericDevice* hwdevice = findBySystemPath(TQString("/sys/devices/system/cpu/cpu%1").arg(processorNumber));
+ // Signal new information available
+ emit hardwareUpdated(hwdevice);
+ }
+ }
+}
+
void TDEHardwareDevices::processModifiedMounts() {
// Detect what changed between the old mount table and the new one,
// and emit appropriate events
@@ -1205,6 +1573,12 @@ TDEGenericDeviceType::TDEGenericDeviceType readGenericDeviceTypeFromString(TQStr
else if (query == "Platform") {
ret = TDEGenericDeviceType::Platform;
}
+ else if (query == "Event") {
+ ret = TDEGenericDeviceType::Event;
+ }
+ else if (query == "Input") {
+ ret = TDEGenericDeviceType::Input;
+ }
else if (query == "PNP") {
ret = TDEGenericDeviceType::PNP;
}
@@ -1479,12 +1853,13 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
TQString systempath(udev_device_get_syspath(dev));
TQString devicevendorid(udev_device_get_property_value(dev, "ID_VENDOR_ID"));
TQString devicemodelid(udev_device_get_property_value(dev, "ID_MODEL_ID"));
+ TQString devicevendoridenc(udev_device_get_property_value(dev, "ID_VENDOR_ENC"));
+ TQString devicemodelidenc(udev_device_get_property_value(dev, "ID_MODEL_ENC"));
TQString devicesubvendorid(udev_device_get_property_value(dev, "ID_SUBVENDOR_ID"));
TQString devicesubmodelid(udev_device_get_property_value(dev, "ID_SUBMODEL_ID"));
TQString devicetypestring(udev_device_get_property_value(dev, "ID_TYPE"));
TQString devicetypestring_alt(udev_device_get_property_value(dev, "DEVTYPE"));
TQString devicepciclass(udev_device_get_property_value(dev, "PCI_CLASS"));
- bool removable = false;
TDEGenericDevice* device = existingdevice;
// FIXME
@@ -1522,12 +1897,13 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
// Go after it manually...
if (devicevendorid.isNull() || devicemodelid.isNull()) {
if (devicemodalias != TQString::null) {
- int vloc = devicemodalias.find("v");
- int dloc = devicemodalias.find("d", vloc);
- int svloc = devicemodalias.find("sv");
- int sdloc = devicemodalias.find("sd", vloc);
// For added fun the device string lengths differ between pci and usb
if (devicemodalias.startsWith("pci")) {
+ int vloc = devicemodalias.find("v");
+ int dloc = devicemodalias.find("d", vloc);
+ int svloc = devicemodalias.find("sv");
+ int sdloc = devicemodalias.find("sd", vloc);
+
devicevendorid = devicemodalias.mid(vloc+1, 8).lower();
devicemodelid = devicemodalias.mid(dloc+1, 8).lower();
if (svloc != -1) {
@@ -1540,6 +1916,11 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
devicesubmodelid.remove(0,4);
}
if (devicemodalias.startsWith("usb")) {
+ int vloc = devicemodalias.find("v");
+ int dloc = devicemodalias.find("p", vloc);
+ int svloc = devicemodalias.find("sv");
+ int sdloc = devicemodalias.find("sp", vloc);
+
devicevendorid = devicemodalias.mid(vloc+1, 4).lower();
devicemodelid = devicemodalias.mid(dloc+1, 4).lower();
if (svloc != -1) {
@@ -1579,11 +1960,43 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
// Classify generic device type and create appropriate object
- // Pull out all event special devices and stuff them under Platform
+ // Pull out all event special devices and stuff them under Event
TQString syspath_tail = systempath.lower();
syspath_tail.remove(0, syspath_tail.findRev("/")+1);
if (syspath_tail.startsWith("event")) {
- if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Platform);
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Event);
+ }
+ // Pull out all input special devices and stuff them under Input
+ if (syspath_tail.startsWith("input")) {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Input);
+ }
+
+ // Check for keyboard
+ // Linux doesn't actually ID the keyboard device itself as such, it instead IDs the input device that is underneath the actual keyboard itseld
+ // Therefore we need to scan <syspath>/input/input* for the ID_INPUT_KEYBOARD attribute
+ bool is_keyboard = false;
+ TQString inputtopdirname = udev_device_get_syspath(dev);
+ inputtopdirname.append("/input/");
+ TQDir inputdir(inputtopdirname);
+ inputdir.setFilter(TQDir::All);
+ const TQFileInfoList *dirlist = inputdir.entryInfoList();
+ if (dirlist) {
+ TQFileInfoListIterator inputdirsit(*dirlist);
+ TQFileInfo *dirfi;
+ while ( (dirfi = inputdirsit.current()) != 0 ) {
+ if ((dirfi->fileName() != ".") && (dirfi->fileName() != "..")) {
+ struct udev_device *slavedev;
+ slavedev = udev_device_new_from_syspath(m_udevStruct, (inputtopdirname + dirfi->fileName()).ascii());
+ if (udev_device_get_property_value(slavedev, "ID_INPUT_KEYBOARD") != 0) {
+ is_keyboard = true;
+ }
+ udev_device_unref(slavedev);
+ }
+ ++inputdirsit;
+ }
+ }
+ if (is_keyboard) {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Keyboard);
}
// Classify specific known devices
@@ -1597,7 +2010,38 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
}
else if (devicetype.isNull()) {
if (devicesubsystem == "acpi") {
- if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::OtherACPI);
+ // If the ACPI device exposes a system path ending in /PNPxxxx:yy, the device type can be precisely determined
+ // See ftp://ftp.microsoft.com/developr/drg/plug-and-play/devids.txt for more information
+ TQString pnpgentype = systempath;
+ pnpgentype.remove(0, pnpgentype.findRev("/")+1);
+ pnpgentype.truncate(pnpgentype.find(":"));
+ if (pnpgentype.startsWith("PNP")) {
+ // We support a limited number of specific PNP IDs here
+ if (pnpgentype == "PNP0C0A") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Battery);
+ }
+ else if (pnpgentype == "PNP0C0B") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::ThermalControl);
+ }
+ else if (pnpgentype == "PNP0C0C") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Power);
+ }
+ else if (pnpgentype == "PNP0C0D") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Power);
+ }
+ else if (pnpgentype == "PNP0C0E") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Power);
+ }
+ else if (pnpgentype == "PNP0C11") {
+ if (!device) device = new TDESensorDevice(TDEGenericDeviceType::ThermalSensor);
+ }
+ else {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::OtherACPI);
+ }
+ }
+ else {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::OtherACPI);
+ }
}
else if (devicesubsystem == "input") {
// Figure out if this device is a mouse, keyboard, or something else
@@ -1640,7 +2084,7 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
else if (devicesubsystem == "hwmon") {
// FIXME
// This might pick up thermal sensors
- if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::OtherSensor);
+ if (!device) device = new TDESensorDevice(TDEGenericDeviceType::OtherSensor);
}
}
@@ -1709,9 +2153,22 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::PNP);
}
if ((devicesubsystem == "hid")
- || (devicesubsystem == "hidraw")) {
+ || (devicesubsystem == "hidraw")
+ || (devicesubsystem == "usbhid")) {
if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::HID);
}
+ if (devicesubsystem == "power_supply") {
+ TQString powersupplyname(udev_device_get_property_value(dev, "POWER_SUPPLY_NAME"));
+ if (powersupplyname.upper().startsWith("AC")) {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Power);
+ }
+ else {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Battery);
+ }
+ }
+ if (devicesubsystem == "backlight") {
+ if (!device) device = new TDEGenericDevice(TDEGenericDeviceType::Power);
+ }
// Moderate accuracy classification, if PCI device class is available
// See http://www.acm.uiuc.edu/sigops/roll_your_own/7.c.1.html for codes and meanings
@@ -1773,6 +2230,8 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
device->setSystemPath(systempath);
device->setVendorID(devicevendorid);
device->setModelID(devicemodelid);
+ device->setVendorEncoded(devicevendoridenc);
+ device->setModelEncoded(devicemodelidenc);
device->setSubVendorID(devicesubvendorid);
device->setSubModelID(devicesubmodelid);
device->setModuleAlias(devicemodalias);
@@ -1788,18 +2247,37 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
}
if (device->type() == TDEGenericDeviceType::Disk) {
- // Determine if disk is removable
- TQString removablenodename = udev_device_get_syspath(dev);
- removablenodename.append("/removable");
- FILE *fp = fopen(removablenodename.ascii(),"r");
- if (fp) {
- if (fgetc(fp) == '1') {
- removable = true;
- }
- fclose(fp);
+ bool removable = false;
+ bool hotpluggable = false;
+
+ // We can get the removable flag, but we have no idea if the device has the ability to notify on media insertion/removal
+ // If there is no such notification possible, then we should not set the removable flag
+ // udev can be such an amazing pain at times
+ // It exports a /capabilities node with no info on what the bits actually mean
+ // This information is very poorly documented as a set of #defines in include/linux/genhd.h
+ // We are specifically interested in GENHD_FL_REMOVABLE and GENHD_FL_MEDIA_CHANGE_NOTIFY
+ // The "removable" flag should also really be renamed to "hotpluggable", as that is far more precise...
+ TQString capabilitynodename = systempath;
+ capabilitynodename.append("/capability");
+ TQFile capabilityfile( capabilitynodename );
+ unsigned int capabilities = 0;
+ if ( capabilityfile.open( IO_ReadOnly ) ) {
+ TQTextStream stream( &capabilityfile );
+ TQString capabilitystring;
+ capabilitystring = stream.readLine();
+ capabilities = capabilitystring.toUInt();
+ capabilityfile.close();
+ }
+ if (capabilities & GENHD_FL_REMOVABLE) {
+ // FIXME
+ // For added fun this is not always true; i.e. GENHD_FL_REMOVABLE can be set when the device cannot be hotplugged (floppy drives).
+ hotpluggable = true;
+ }
+ if (capabilities & GENHD_FL_MEDIA_CHANGE_NOTIFY) {
+ removable = true;
}
- // See if any other devices are exclusively using this device, such as the Device Mapper|
+ // See if any other devices are exclusively using this device, such as the Device Mapper
TQStringList holdingDeviceNodes;
TQString holdersnodename = udev_device_get_syspath(dev);
holdersnodename.append("/holders/");
@@ -1955,6 +2433,9 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
if (removable) {
diskstatus = diskstatus | TDEDiskDeviceStatus::Removable;
}
+ if (hotpluggable) {
+ diskstatus = diskstatus | TDEDiskDeviceStatus::Hotpluggable;
+ }
if ((filesystemtype.upper() != "CRYPTO_LUKS") && (filesystemtype.upper() != "CRYPTO") && (!filesystemtype.isNull())) {
diskstatus = diskstatus | TDEDiskDeviceStatus::ContainsFilesystem;
@@ -1962,7 +2443,7 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
// Set mountable flag if device is likely to be mountable
diskstatus = diskstatus | TDEDiskDeviceStatus::Mountable;
- if ((!devicetypestring.upper().isNull()) && (disktype & TDEDiskDeviceType::HDD)) {
+ if ((devicetypestring.upper().isNull()) && (disktype & TDEDiskDeviceType::HDD)) {
diskstatus = diskstatus & ~TDEDiskDeviceStatus::Mountable;
}
if (removable) {
@@ -2035,12 +2516,65 @@ TDEGenericDevice* TDEHardwareDevices::classifyUnknownDevice(udev_device* dev, TD
devicenode.remove(0, devicenode.findRev("/")+1);
}
+ if ((device->type() == TDEGenericDeviceType::OtherSensor) || (device->type() == TDEGenericDeviceType::ThermalSensor)) {
+ // Populate all sensor values
+ TDESensorClusterMap sensors;
+ TQString valuesnodename = systempath + "/";
+ TQDir valuesdir(valuesnodename);
+ valuesdir.setFilter(TQDir::All);
+ TQString nodename;
+ const TQFileInfoList *dirlist = valuesdir.entryInfoList();
+ if (dirlist) {
+ TQFileInfoListIterator valuesdirit(*dirlist);
+ TQFileInfo *dirfi;
+ while ( (dirfi = valuesdirit.current()) != 0 ) {
+ nodename = dirfi->fileName();
+ if (nodename.contains("_")) {
+ TQFile file( valuesnodename + nodename );
+ if ( file.open( IO_ReadOnly ) ) {
+ TQTextStream stream( &file );
+ TQString line;
+ line = stream.readLine();
+ TQStringList sensornodelist = TQStringList::split("_", nodename);
+ TQString sensornodename = *(sensornodelist.at(0));
+ TQString sensornodetype = *(sensornodelist.at(1));
+ if (sensornodetype == "label") {
+ sensors[sensornodename].label = line;
+ }
+ if (sensornodetype == "input") {
+ sensors[sensornodename].current = line.toDouble();
+ }
+ if (sensornodetype == "min") {
+ sensors[sensornodename].minimum = line.toDouble();
+ }
+ if (sensornodetype == "max") {
+ sensors[sensornodename].maximum = line.toDouble();
+ }
+ if (sensornodetype == "warn") {
+ sensors[sensornodename].warning = line.toDouble();
+ }
+ if (sensornodetype == "crit") {
+ sensors[sensornodename].critical = line.toDouble();
+ }
+ file.close();
+ }
+ }
+ ++valuesdirit;
+ }
+ }
+
+ TDESensorDevice* sdevice = dynamic_cast<TDESensorDevice*>(device);
+ sdevice->setValues(sensors);
+ }
+
// Set basic device information again, as some information may have changed
device->setName(devicename);
device->setDeviceNode(devicenode);
device->setSystemPath(systempath);
device->setVendorID(devicevendorid);
device->setModelID(devicemodelid);
+ device->setVendorEncoded(devicevendoridenc);
+ device->setModelEncoded(devicemodelidenc);
device->setSubVendorID(devicesubvendorid);
device->setSubModelID(devicesubmodelid);
device->setDeviceDriver(devicedriver);
@@ -2189,23 +2723,28 @@ void TDEHardwareDevices::addCoreSystemDevices() {
while ( !stream.atEnd() ) {
line = stream.readLine();
// WARNING This routine assumes that "processor" is always the first entry in /proc/cpuinfo!
+ // FIXME Parse all available information, such as frequency, etc.
if (line.startsWith("processor")) {
line.remove(0, line.find(":")+1);
line = line.stripWhiteSpace();
processorNumber = line.toInt();
- hwdevice = new TDEGenericDevice(TDEGenericDeviceType::CPU);
- hwdevice->setSystemPath(TQString("/sys/devices/cpu/cpu%1").arg(processorNumber)); // FIXME: A system path is required, but I can't give a real, extant, unique path to the hardware manager due to kernel limitations
+ hwdevice = new TDECPUDevice(TDEGenericDeviceType::CPU);
+ hwdevice->setSystemPath(TQString("/sys/devices/system/cpu/cpu%1").arg(processorNumber));
m_deviceList.append(hwdevice);
- }
- if (line.startsWith("model name")) {
- line.remove(0, line.find(":")+1);
- line = line.stripWhiteSpace();
- hwdevice->setName(line);
+#if 0
+ // Set up CPU information monitor
+ // The only way CPU information can be changed is if something changes in the cpufreq node
+ // This may change in the future, but for now it is a fairly good assumption
+ m_cpuWatch->addDir(TQString("/sys/devices/system/cpu/cpu%1/cpufreq").arg(processorNumber));
+#endif
}
lines += line;
}
file.close();
}
+
+ // Populate CPU information
+ processModifiedCPUs();
}
TQString TDEHardwareDevices::findPCIDeviceName(TQString vendorid, TQString modelid, TQString subvendorid, TQString submodelid) {
@@ -2428,6 +2967,76 @@ TQString TDEHardwareDevices::findUSBDeviceName(TQString vendorid, TQString model
}
}
+TQString TDEHardwareDevices::findPNPDeviceName(TQString pnpid) {
+ TQString friendlyName = TQString::null;
+
+ if (!pnp_id_map) {
+ pnp_id_map = new TDEDeviceIDMap;
+
+ TQStringList hardware_info_directories(KGlobal::dirs()->resourceDirs("data"));
+ TQString hardware_info_directory_suffix("tdehwlib/pnpdev/");
+ TQString hardware_info_directory;
+ TQString database_filename;
+
+ for ( TQStringList::Iterator it = hardware_info_directories.begin(); it != hardware_info_directories.end(); ++it ) {
+ hardware_info_directory = (*it);
+ hardware_info_directory += hardware_info_directory_suffix;
+
+ if (KGlobal::dirs()->exists(hardware_info_directory)) {
+ database_filename = hardware_info_directory + "pnp.ids";
+ if (TQFile::exists(database_filename)) {
+ break;
+ }
+ }
+ }
+
+ if (!TQFile::exists(database_filename)) {
+ printf("[tdehardwaredevices] Unable to locate PNP information database pnp.ids\n\r"); fflush(stdout);
+ return i18n("Unknown PNP Device");
+ }
+
+ TQFile database(database_filename);
+ if (database.open(IO_ReadOnly)) {
+ TQTextStream stream(&database);
+ TQString line;
+ TQString pnpID;
+ TQString vendorName;
+ TQString deviceMapKey;
+ TQStringList devinfo;
+ while (!stream.atEnd()) {
+ line = stream.readLine();
+ if ((!line.upper().startsWith("\t")) && (!line.upper().startsWith("#"))) {
+ devinfo = TQStringList::split('\t', line, false);
+ if (devinfo.count() > 1) {
+ pnpID = *(devinfo.at(0));
+ vendorName = *(devinfo.at(1));;
+ vendorName = vendorName.stripWhiteSpace();
+ deviceMapKey = pnpID.upper().stripWhiteSpace();
+ if (!deviceMapKey.isNull()) {
+ pnp_id_map->insert(deviceMapKey, vendorName, true);
+ }
+ }
+ }
+ }
+ database.close();
+ }
+ else {
+ printf("[tdehardwaredevices] Unable to open PNP information database %s\n\r", database_filename.ascii()); fflush(stdout);
+ }
+ }
+
+ if (pnp_id_map) {
+ TQString deviceName;
+
+ deviceName = (*pnp_id_map)[pnpid];
+
+ return deviceName;
+ }
+ else {
+ return i18n("Unknown PNP Device");
+ }
+}
+
TQString TDEHardwareDevices::getFriendlyDeviceTypeStringFromType(TDEGenericDeviceType::TDEGenericDeviceType query) {
TQString ret = "Unknown Device";
@@ -2528,6 +3137,12 @@ TQString TDEHardwareDevices::getFriendlyDeviceTypeStringFromType(TDEGenericDevic
else if (query == TDEGenericDeviceType::Platform) {
ret = i18n("Platform");
}
+ else if (query == TDEGenericDeviceType::Event) {
+ ret = i18n("Platform Event");
+ }
+ else if (query == TDEGenericDeviceType::Input) {
+ ret = i18n("Platform Input");
+ }
else if (query == TDEGenericDeviceType::PNP) {
ret = i18n("Plug and Play");
}
@@ -2656,6 +3271,12 @@ TQPixmap TDEHardwareDevices::getDeviceTypeIconFromType(TDEGenericDeviceType::TDE
else if (query == TDEGenericDeviceType::Platform) {
ret = DesktopIcon("kcmsystem", size);
}
+ else if (query == TDEGenericDeviceType::Event) {
+ ret = DesktopIcon("kcmsystem", size);
+ }
+ else if (query == TDEGenericDeviceType::Input) {
+ ret = DesktopIcon("kcmsystem", size);
+ }
else if (query == TDEGenericDeviceType::PNP) {
ret = DesktopIcon("kcmsystem", size);
}
diff --git a/tdecore/tdehardwaredevices.h b/tdecore/tdehardwaredevices.h
index f284a7d0c..0073cb454 100644
--- a/tdecore/tdehardwaredevices.h
+++ b/tdecore/tdehardwaredevices.h
@@ -74,6 +74,8 @@ enum TDEGenericDeviceType {
ThermalControl,
Bridge,
Platform,
+ Event,
+ Input,
PNP,
OtherACPI,
OtherUSB,
@@ -149,6 +151,7 @@ enum TDEDiskDeviceStatus {
UsedByDevice = 0x00000010,
UsesDevice = 0x00000020,
ContainsFilesystem = 0x00000040,
+ Hotpluggable = 0x00000080,
Other = 0x80000000
};
@@ -168,6 +171,22 @@ inline TDEDiskDeviceStatus operator~(TDEDiskDeviceStatus a)
}
};
+class TDECORE_EXPORT TDESensorCluster
+{
+ public:
+ /**
+ * Constructor.
+ */
+ TDESensorCluster();
+
+ TQString label;
+ double current;
+ double minimum;
+ double maximum;
+ double warning;
+ double critical;
+};
+
class TDECORE_EXPORT TDEGenericDevice
{
public:
@@ -299,6 +318,26 @@ class TDECORE_EXPORT TDEGenericDevice
void setModelID(TQString id);
/**
+ * @return a TQString with the encoded vendor, if any
+ */
+ TQString &vendorEncoded();
+
+ /**
+ * @param a TQString with the encoded vendor, if any
+ */
+ void setVendorEncoded(TQString id);
+
+ /**
+ * @return a TQString with the encoded model, if any
+ */
+ TQString &modelEncoded();
+
+ /**
+ * @param a TQString with the encoded model, if any
+ */
+ void setModelEncoded(TQString id);
+
+ /**
* @return a TQString with the subvendor ID, if any
*/
TQString &subVendorID();
@@ -396,6 +435,8 @@ class TDECORE_EXPORT TDEGenericDevice
TQString m_uniqueID;
TQString m_vendorID;
TQString m_modelID;
+ TQString m_vendorenc;
+ TQString m_modelenc;
TQString m_subvendorID;
TQString m_submodelID;
TQString m_pciClass;
@@ -613,10 +654,146 @@ class TDECORE_EXPORT TDEStorageDevice : public TDEGenericDevice
TQStringList m_slaveDevices;
};
+class TDECORE_EXPORT TDECPUDevice : public TDEGenericDevice
+{
+ public:
+ /**
+ * Constructor.
+ * @param Device type
+ */
+ TDECPUDevice(TDEGenericDeviceType::TDEGenericDeviceType dt, TQString dn=TQString::null);
+
+ /**
+ * Destructor.
+ */
+ ~TDECPUDevice();
+
+ /**
+ * @return a double with the current CPU frequency in MHz, if available
+ */
+ double &frequency();
+
+ /**
+ * @param a double with the current CPU frequency in MHz, if available
+ */
+ void setFrequency(double fr);
+
+ /**
+ * @return a double with the minimum CPU frequency in MHz, if available
+ */
+ double &minFrequency();
+
+ /**
+ * @param a double with the minimum CPU frequency in MHz, if available
+ */
+ void setMinFrequency(double fr);
+
+ /**
+ * @return a double with the maximum CPU frequency in MHz, if available
+ */
+ double &maxFrequency();
+
+ /**
+ * @param a double with the maximum CPU frequency in MHz, if available
+ */
+ void setMaxFrequency(double fr);
+
+ /**
+ * @return a double with the transition latency in ns, if available
+ */
+ double &transitionLatency();
+
+ /**
+ * @param a double with the transition latency in ns, if available
+ */
+ void setTransitionLatency(double tl);
+
+ /**
+ * @return a TQString with the current CPU governor policy, if available
+ */
+ TQString &governor();
+
+ /**
+ * @param a TQString with the current CPU governor policy, if available
+ */
+ void setGovernor(TQString gr);
+
+ /**
+ * @return a TQString with the current CPU scaling driver, if available
+ */
+ TQString &scalingDriver();
+
+ /**
+ * @param a TQString with the current CPU scaling driver, if available
+ */
+ void setScalingDriver(TQString dr);
+
+ /**
+ * @return a TQStringList with the IDs of all processors that are dependent on the frequency/power settings of this one, if available
+ */
+ TQStringList &dependentProcessors();
+
+ /**
+ * @param a TQStringList with the IDs of all processors that are dependent on the frequency/power settings of this one, if available
+ */
+ void setDependentProcessors(TQStringList dp);
+
+ /**
+ * @return a TQStringList with all valid scaling frequencies in Hz, if available
+ */
+ TQStringList &availableFrequencies();
+
+ /**
+ * @param a TQStringList with all valid scaling frequencies in Hz, if available
+ */
+ void setAvailableFrequencies(TQStringList af);
+
+ private:
+ double m_frequency;
+ double m_minfrequency;
+ double m_maxfrequency;
+ double m_transitionlatency;
+ TQString m_governor;
+ TQString m_scalingdriver;
+ TQStringList m_tiedprocs;
+ TQStringList m_frequencies;
+};
+
+typedef TQMap<TQString, TDESensorCluster> TDESensorClusterMap;
+
+class TDECORE_EXPORT TDESensorDevice : public TDEGenericDevice
+{
+ public:
+ /**
+ * Constructor.
+ * @param Device type
+ */
+ TDESensorDevice(TDEGenericDeviceType::TDEGenericDeviceType dt, TQString dn=TQString::null);
+
+ /**
+ * Destructor.
+ */
+ ~TDESensorDevice();
+
+ /**
+ * @return a TDESensorClusterMap with the current sensor values
+ */
+ TDESensorClusterMap values();
+
+ /**
+ * @param a TDESensorClusterMap with the current sensor values
+ */
+ void setValues(TDESensorClusterMap cl);
+
+ private:
+ TDESensorClusterMap m_sensorValues;
+};
+
typedef TQPtrList<TDEGenericDevice> TDEGenericHardwareList;
typedef TQMap<TQString, TQString> TDEDeviceIDMap;
class TQSocketNotifier;
+class KSimpleDirWatch;
class TDECORE_EXPORT TDEHardwareDevices : public TQObject
{
@@ -697,6 +874,13 @@ class TDECORE_EXPORT TDEHardwareDevices : public TQObject
TQString findUSBDeviceName(TQString vendorid, TQString modelid, TQString subvendorid, TQString submodelid);
/**
+ * Look up the device in the system PNP database
+ * @param pnpid a TQString containing the PNP ID
+ * @return a TQString containing the device name, if found
+ */
+ TQString findPNPDeviceName(TQString pnpid);
+
+ /**
* Get a friendly string describing a device type
* @param query a TDEGenericDeviceType::TDEGenericDeviceType specifying a device type
* @return a TQString containing the friendly type name
@@ -720,6 +904,7 @@ class TDECORE_EXPORT TDEHardwareDevices : public TQObject
private slots:
void processHotPluggedHardware();
void processModifiedMounts();
+ void processModifiedCPUs();
private:
void rescanDeviceInformation(TDEGenericDevice* hwdevice);
@@ -738,14 +923,18 @@ class TDECORE_EXPORT TDEHardwareDevices : public TQObject
struct udev_monitor *m_udevMonitorStruct;
TDEGenericHardwareList m_deviceList;
int m_procMountsFd;
+ KSimpleDirWatch* m_cpuWatch;
+ TQTimer* m_cpuWatchTimer;
TQSocketNotifier* m_devScanNotifier;
TQSocketNotifier* m_mountScanNotifier;
TQStringList m_mountTable;
+ TQStringList m_cpuInfo;
TDEDeviceIDMap* pci_id_map;
TDEDeviceIDMap* usb_id_map;
+ TDEDeviceIDMap* pnp_id_map;
friend class TDEGenericDevice;
friend class TDEStorageDevice;